@okyrychenko-dev/react-action-guard 0.5.0 → 0.6.1

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/dist/index.d.cts CHANGED
@@ -13,19 +13,131 @@ interface UseAsyncActionOptions {
13
13
  onTimeout?: (blockerId: string) => void;
14
14
  }
15
15
  /**
16
- * Hook to wrap async functions with automatic blocking/unblocking
16
+ * Wraps async functions with automatic UI blocking during execution.
17
17
  *
18
- * Creates a wrapper function that automatically adds a blocker before executing
19
- * the async function and removes it after completion (success or failure).
18
+ * Creates a wrapper function that automatically:
19
+ * 1. Adds a blocker before executing the async function
20
+ * 2. Executes the async function
21
+ * 3. Removes the blocker after completion (success or failure)
20
22
  *
21
- * Supports both global store and context store (via UIBlockingProvider).
23
+ * Each invocation generates a unique blocker ID (actionId-counter), allowing
24
+ * multiple concurrent executions of the same action without ID conflicts.
25
+ * The blocker is removed even if the async function throws an error.
22
26
  *
23
- * @template T - The return type of the async function
24
- * @param actionId - Unique identifier for the action (used in blocker ID and reason)
25
- * @param scope - Scope(s) to block during execution (default: "global")
26
- * @param options - Optional configuration including timeout
27
- * @returns Function that wraps async functions with blocking behavior
27
+ * Works with both the global store and isolated provider instances (via UIBlockingProvider).
28
28
  *
29
+ * @template T - The return type of the async function being wrapped
30
+ * @param actionId - Base identifier for the action. Used in blocker ID and default reason.
31
+ * Example: "saveForm", "fetchData", "submitOrder"
32
+ * @param scope - Scope(s) to block during execution. Can be a single scope or array.
33
+ * Defaults to "global" if not specified.
34
+ * @param options - Optional configuration
35
+ * @param options.timeout - Timeout in milliseconds. Blocker auto-removes after this duration.
36
+ * @param options.onTimeout - Callback invoked when blocker times out before async completes
37
+ *
38
+ * @returns Wrapper function that accepts an async function and returns a Promise with the same type
39
+ *
40
+ * @example
41
+ * Basic form submission
42
+ * ```tsx
43
+ * function MyForm() {
44
+ * const executeWithBlocking = useAsyncAction('saveForm', 'form');
45
+ *
46
+ * const handleSubmit = async (e) => {
47
+ * e.preventDefault();
48
+ *
49
+ * await executeWithBlocking(async () => {
50
+ * const response = await fetch('/api/save', {
51
+ * method: 'POST',
52
+ * body: JSON.stringify(formData)
53
+ * });
54
+ * return response.json();
55
+ * });
56
+ *
57
+ * alert('Form saved!');
58
+ * };
59
+ *
60
+ * return <form onSubmit={handleSubmit}>...</form>;
61
+ * }
62
+ * ```
63
+ *
64
+ * @example
65
+ * With timeout and error handling
66
+ * ```tsx
67
+ * const executeWithBlocking = useAsyncAction(
68
+ * 'fetchData',
69
+ * ['global', 'data'],
70
+ * {
71
+ * timeout: 30000, // 30 seconds
72
+ * onTimeout: (blockerId) => {
73
+ * console.warn(`Operation ${blockerId} timed out`);
74
+ * }
75
+ * }
76
+ * );
77
+ *
78
+ * const loadData = async () => {
79
+ * try {
80
+ * const data = await executeWithBlocking(async () => {
81
+ * return await fetchDataFromAPI();
82
+ * });
83
+ * setData(data);
84
+ * } catch (error) {
85
+ * console.error('Failed to load data:', error);
86
+ * // Blocker is automatically removed even on error
87
+ * }
88
+ * };
89
+ * ```
90
+ *
91
+ * @example
92
+ * Multiple concurrent actions
93
+ * ```tsx
94
+ * function FileUploader() {
95
+ * const uploadWithBlocking = useAsyncAction('upload', 'uploads');
96
+ *
97
+ * const uploadFiles = async (files) => {
98
+ * // Each upload gets unique blocker ID (upload-1, upload-2, etc.)
99
+ * const promises = files.map(file =>
100
+ * uploadWithBlocking(() => uploadFile(file))
101
+ * );
102
+ *
103
+ * await Promise.all(promises);
104
+ * };
105
+ *
106
+ * return <...>;
107
+ * }
108
+ * ```
109
+ *
110
+ * @example
111
+ * Type-safe return value
112
+ * ```tsx
113
+ * interface UserData {
114
+ * id: string;
115
+ * name: string;
116
+ * }
117
+ *
118
+ * function UserProfile() {
119
+ * const executeWithBlocking = useAsyncAction<UserData>('fetchUser', 'user');
120
+ *
121
+ * const loadUser = async (userId: string) => {
122
+ * const userData = await executeWithBlocking(async () => {
123
+ * const response = await fetch(`/api/users/${userId}`);
124
+ * return response.json(); // TypeScript knows this is UserData
125
+ * });
126
+ *
127
+ * // userData is typed as UserData
128
+ * console.log(userData.name);
129
+ * };
130
+ *
131
+ * return <...>;
132
+ * }
133
+ * ```
134
+ *
135
+ * @see {@link useBlocker} for manual blocker management
136
+ * @see {@link useIsBlocked} to check blocking state
137
+ * @see {@link UseAsyncActionOptions} for available options
138
+ *
139
+ * @public
140
+ * @since 0.6.0
29
141
  */
30
142
  declare const useAsyncAction: <T = unknown>(actionId: string, scope?: string | ReadonlyArray<string>, options?: UseAsyncActionOptions) => ((asyncFn: () => Promise<T>) => Promise<T>);
31
143
 
@@ -45,7 +157,7 @@ interface CustomAnalyticsConfig {
45
157
  }
46
158
  type AnalyticsConfig = GoogleAnalyticsConfig | MixpanelConfig | AmplitudeConfig | CustomAnalyticsConfig;
47
159
 
48
- type BlockingAction = "add" | "remove" | "update" | "cancel" | "timeout";
160
+ type BlockingAction = "add" | "update" | "remove" | "timeout" | "clear" | "clear_scope";
49
161
  type MiddlewareBlockerConfig = BlockerConfig;
50
162
  interface MiddlewareContext {
51
163
  action: BlockingAction;
@@ -53,22 +165,200 @@ interface MiddlewareContext {
53
165
  config?: MiddlewareBlockerConfig;
54
166
  timestamp: number;
55
167
  prevState?: MiddlewareBlockerConfig;
168
+ scope?: string;
169
+ count?: number;
56
170
  }
57
171
  type Middleware = (context: MiddlewareContext) => void | Promise<void>;
58
172
 
173
+ /**
174
+ * Creates middleware for tracking blocker events to analytics platforms.
175
+ *
176
+ * Automatically tracks all blocker actions (add, remove, update, timeout, clear)
177
+ * to your analytics platform. Supports Google Analytics, Mixpanel, Amplitude,
178
+ * and custom analytics providers.
179
+ *
180
+ * Event data includes: blockerId, action, scope, reason, priority, timestamp,
181
+ * and duration (for remove events).
182
+ *
183
+ * @param config - Analytics configuration specifying provider and credentials
184
+ *
185
+ * @example
186
+ * Google Analytics integration
187
+ * ```ts
188
+ * import { configureMiddleware, createAnalyticsMiddleware } from '@okyrychenko-dev/react-action-guard';
189
+ *
190
+ * const analyticsMiddleware = createAnalyticsMiddleware({
191
+ * provider: 'ga',
192
+ * trackingId: 'GA-XXXXX-Y'
193
+ * });
194
+ *
195
+ * configureMiddleware([analyticsMiddleware]);
196
+ * ```
197
+ *
198
+ * @example
199
+ * Mixpanel integration
200
+ * ```ts
201
+ * const analyticsMiddleware = createAnalyticsMiddleware({
202
+ * provider: 'mixpanel',
203
+ * token: 'your_mixpanel_token',
204
+ * options: {
205
+ * debug: true
206
+ * }
207
+ * });
208
+ *
209
+ * configureMiddleware([analyticsMiddleware]);
210
+ * ```
211
+ *
212
+ * @example
213
+ * Custom analytics provider
214
+ * ```ts
215
+ * const analyticsMiddleware = createAnalyticsMiddleware({
216
+ * track: (eventName, eventData) => {
217
+ * // Send to your custom analytics
218
+ * myAnalytics.track(eventName, {
219
+ * ...eventData,
220
+ * userId: getCurrentUserId()
221
+ * });
222
+ * }
223
+ * });
224
+ * ```
225
+ *
226
+ * @example
227
+ * Amplitude with user properties
228
+ * ```ts
229
+ * const analyticsMiddleware = createAnalyticsMiddleware({
230
+ * provider: 'amplitude',
231
+ * apiKey: 'your_amplitude_key',
232
+ * userId: 'user-123',
233
+ * userProperties: {
234
+ * plan: 'premium',
235
+ * role: 'admin'
236
+ * }
237
+ * });
238
+ * ```
239
+ *
240
+ * @see {@link AnalyticsConfig} for configuration options
241
+ * @see {@link configureMiddleware} for registering middleware
242
+ *
243
+ * @public
244
+ * @since 0.6.0
245
+ */
59
246
  declare const createAnalyticsMiddleware: (config: AnalyticsConfig) => Middleware;
60
247
 
61
248
  /**
62
- * Configure middleware for the UI blocking store
249
+ * Configures and registers multiple middleware functions for the UI blocking store.
250
+ *
251
+ * Convenience function that registers an array of middleware at once. Each middleware
252
+ * is automatically assigned a unique name based on its array index ("middleware-0",
253
+ * "middleware-1", etc.). Middleware are executed in the order they are registered.
254
+ *
255
+ * All middleware receive {@link MiddlewareContext} containing action type, blocker ID,
256
+ * configuration, and timestamp for each blocking event.
63
257
  *
64
- * This is a convenience function that registers multiple middleware at once.
65
- * Each middleware will be registered with an auto-generated name based on its index.
258
+ * @param middlewares - Array of middleware functions to register. Functions are called
259
+ * in order for each blocking action (add, remove, update, etc.)
66
260
  *
67
- * @param middlewares - Array of middleware functions to register
261
+ * @example
262
+ * Basic usage with built-in middleware
263
+ * ```ts
264
+ * import { configureMiddleware, loggerMiddleware, createPerformanceMiddleware } from '@okyrychenko-dev/react-action-guard';
265
+ *
266
+ * configureMiddleware([
267
+ * loggerMiddleware,
268
+ * createPerformanceMiddleware({ threshold: 1000 })
269
+ * ]);
270
+ * ```
271
+ *
272
+ * @example
273
+ * With custom middleware
274
+ * ```ts
275
+ * const customMiddleware: Middleware = (context) => {
276
+ * if (context.action === 'add') {
277
+ * console.log(`Blocker added: ${context.blockerId}`);
278
+ * }
279
+ * };
280
+ *
281
+ * configureMiddleware([
282
+ * loggerMiddleware,
283
+ * customMiddleware
284
+ * ]);
285
+ * ```
286
+ *
287
+ * @example
288
+ * Analytics integration
289
+ * ```ts
290
+ * import { configureMiddleware, createAnalyticsMiddleware } from '@okyrychenko-dev/react-action-guard';
68
291
  *
292
+ * configureMiddleware([
293
+ * createAnalyticsMiddleware({
294
+ * provider: 'google-analytics',
295
+ * trackingId: 'GA-XXXXX'
296
+ * })
297
+ * ]);
298
+ * ```
299
+ *
300
+ * @see {@link Middleware} for middleware function signature
301
+ * @see {@link loggerMiddleware} for built-in logging middleware
302
+ * @see {@link createAnalyticsMiddleware} for analytics tracking
303
+ * @see {@link createPerformanceMiddleware} for performance monitoring
304
+ *
305
+ * @public
306
+ * @since 0.6.0
69
307
  */
70
308
  declare function configureMiddleware(middlewares: ReadonlyArray<Middleware>): void;
71
309
 
310
+ /**
311
+ * Built-in middleware for logging all blocking actions to the console.
312
+ *
313
+ * Logs every blocker action (add, remove, update, timeout, clear) with emoji
314
+ * indicators and formatted data. Useful for debugging blocking behavior during
315
+ * development. Automatically disabled in production builds for performance.
316
+ *
317
+ * Output format: `[UIBlocking] 🔒 blocker-id {scope: "form", reason: "..."}`
318
+ *
319
+ * Emoji indicators:
320
+ * - 🔒 Add
321
+ * - 🔓 Remove
322
+ * - 🔄 Update
323
+ * - ⏰ Timeout
324
+ * - 🧹 Clear/ClearScope
325
+ *
326
+ * @example
327
+ * Register logger middleware
328
+ * ```ts
329
+ * import { configureMiddleware, loggerMiddleware } from '@okyrychenko-dev/react-action-guard';
330
+ *
331
+ * // Enable logging for all blocking actions
332
+ * configureMiddleware([loggerMiddleware]);
333
+ * ```
334
+ *
335
+ * @example
336
+ * Console output examples
337
+ * ```
338
+ * [UIBlocking] 🔒 save-form {scope: "form", reason: "Saving...", priority: 50}
339
+ * [UIBlocking] 🔄 save-form {scope: "form", reason: "Almost done..."}
340
+ * [UIBlocking] 🔓 save-form {reason: "Saving..."}
341
+ * [UIBlocking] ⏰ api-timeout {timeout: 30000}
342
+ * ```
343
+ *
344
+ * @example
345
+ * Production check pattern
346
+ * ```ts
347
+ * import { configureMiddleware, loggerMiddleware } from '@okyrychenko-dev/react-action-guard';
348
+ * const middlewares = [];
349
+ * if (process.env.NODE_ENV !== 'production') {
350
+ * middlewares.push(loggerMiddleware);
351
+ * }
352
+ * configureMiddleware(middlewares);
353
+ * ```
354
+ *
355
+ * @see {@link configureMiddleware} for registering middleware
356
+ * @see {@link Middleware} for middleware function signature
357
+ * @see {@link MiddlewareContext} for available context data
358
+ *
359
+ * @public
360
+ * @since 0.6.0
361
+ */
72
362
  declare const loggerMiddleware: Middleware;
73
363
 
74
364
  interface PerformanceConfig {
@@ -76,14 +366,133 @@ interface PerformanceConfig {
76
366
  onSlowBlock?: (blockerId: string, duration: number) => void;
77
367
  }
78
368
 
369
+ /**
370
+ * Creates middleware for monitoring blocker performance and detecting slow blocks.
371
+ *
372
+ * Tracks the duration of each blocker (from add to remove) and warns when a blocker
373
+ * exceeds the configured threshold. Useful for identifying performance issues,
374
+ * stuck blockers, or operations that take too long.
375
+ *
376
+ * Automatically logs warnings to console for slow blocks and optionally invokes
377
+ * a callback for custom handling (e.g., error reporting, analytics).
378
+ *
379
+ * @param config - Performance monitoring configuration
380
+ * @param config.slowBlockThreshold - Duration in ms before considering a block "slow".
381
+ * Defaults to 3000ms (3 seconds).
382
+ * @param config.onSlowBlock - Optional callback invoked when a slow block is detected.
383
+ * Receives blockerId and duration in milliseconds.
384
+ *
385
+ * @returns Middleware function for performance monitoring
386
+ *
387
+ * @example
388
+ * Basic usage with default threshold
389
+ * ```ts
390
+ * import { configureMiddleware, createPerformanceMiddleware } from '@okyrychenko-dev/react-action-guard';
391
+ *
392
+ * const performanceMiddleware = createPerformanceMiddleware();
393
+ *
394
+ * configureMiddleware([performanceMiddleware]);
395
+ * // Warns if any blocker lasts > 3 seconds
396
+ * ```
397
+ *
398
+ * @example
399
+ * Custom threshold and callback
400
+ * ```ts
401
+ * const performanceMiddleware = createPerformanceMiddleware({
402
+ * slowBlockThreshold: 5000, // 5 seconds
403
+ * onSlowBlock: (blockerId, duration) => {
404
+ * console.error(`Blocker ${blockerId} took ${duration}ms`);
405
+ *
406
+ * // Report to error tracking
407
+ * Sentry.captureMessage('Slow UI Blocker', {
408
+ * extra: { blockerId, duration }
409
+ * });
410
+ * }
411
+ * });
412
+ * ```
413
+ *
414
+ * @example
415
+ * With analytics integration
416
+ * ```ts
417
+ * const performanceMiddleware = createPerformanceMiddleware({
418
+ * slowBlockThreshold: 2000,
419
+ * onSlowBlock: (blockerId, duration) => {
420
+ * // Track slow blocks in analytics
421
+ * analytics.track('slow_blocker_detected', {
422
+ * blocker_id: blockerId,
423
+ * duration_ms: duration,
424
+ * threshold_ms: 2000
425
+ * });
426
+ * }
427
+ * });
428
+ * ```
429
+ *
430
+ * @example
431
+ * Development vs Production
432
+ * ```ts
433
+ * const performanceMiddleware = createPerformanceMiddleware({
434
+ * // Strict threshold in development
435
+ * slowBlockThreshold: process.env.NODE_ENV === 'production' ? 5000 : 1000,
436
+ * onSlowBlock: (blockerId, duration) => {
437
+ * if (process.env.NODE_ENV === 'production') {
438
+ * // Report to error service in production
439
+ * errorService.log({ blockerId, duration });
440
+ * } else {
441
+ * // Detailed logging in development
442
+ * console.table({ blockerId, duration });
443
+ * }
444
+ * }
445
+ * });
446
+ * ```
447
+ *
448
+ * @see {@link PerformanceConfig} for configuration options
449
+ * @see {@link configureMiddleware} for registering middleware
450
+ *
451
+ * @public
452
+ * @since 0.6.0
453
+ */
79
454
  declare const createPerformanceMiddleware: (config?: PerformanceConfig) => Middleware;
80
455
 
81
456
  /**
82
- * Configuration for a blocker
457
+ * Configuration options for creating or updating a blocker.
458
+ *
459
+ * All fields are optional. Defaults will be applied for any missing values:
460
+ * - `scope`: "global"
461
+ * - `reason`: "Blocking UI"
462
+ * - `priority`: 50
463
+ * - `timestamp`: Current time
464
+ *
465
+ * @example
466
+ * Basic configuration
467
+ * ```ts
468
+ * const config: BlockerConfig = {
469
+ * scope: 'form',
470
+ * reason: 'Submitting data...',
471
+ * priority: 70
472
+ * };
473
+ * ```
474
+ *
475
+ * @example
476
+ * With timeout and callback
477
+ * ```ts
478
+ * const config: BlockerConfig = {
479
+ * scope: ['form', 'navigation'],
480
+ * reason: 'Critical operation in progress',
481
+ * priority: 90,
482
+ * timeout: 30000, // 30 seconds
483
+ * onTimeout: (id) => {
484
+ * console.warn(`Blocker ${id} timed out`);
485
+ * }
486
+ * };
487
+ * ```
488
+ *
489
+ * @public
490
+ * @since 0.6.0
83
491
  */
84
492
  interface BlockerConfig {
85
493
  scope?: string | ReadonlyArray<string>;
86
494
  reason?: string;
495
+ /** Priority level (negative values are normalized to 0) */
87
496
  priority?: number;
88
497
  timestamp?: number;
89
498
  /** Automatically remove the blocker after N milliseconds */
@@ -92,7 +501,16 @@ interface BlockerConfig {
92
501
  onTimeout?: (blockerId: string) => void;
93
502
  }
94
503
  /**
95
- * Stored blocker with all required fields
504
+ * Internal representation of a blocker with all fields populated.
505
+ *
506
+ * This is the stored format in the Map. All optional fields from {@link BlockerConfig}
507
+ * are converted to required fields with default values applied.
508
+ *
509
+ * @internal This type is primarily for internal use. Users should work with
510
+ * {@link BlockerConfig} for configuration and {@link BlockerInfo} for
511
+ * reading blocker data.
512
+ *
513
+ * @since 0.6.0
96
514
  */
97
515
  interface StoredBlocker {
98
516
  scope: string | ReadonlyArray<string>;
@@ -107,23 +525,74 @@ interface StoredBlocker {
107
525
  onTimeout?: (blockerId: string) => void;
108
526
  }
109
527
  /**
110
- * Blocker information including its ID
528
+ * Complete blocker information including its unique identifier.
529
+ *
530
+ * This type extends {@link StoredBlocker} with the blocker's ID, making it
531
+ * suitable for reading and displaying blocker information. Returned by
532
+ * {@link UIBlockingStoreActions.getBlockingInfo} and used by {@link useBlockingInfo}.
533
+ *
534
+ * @example
535
+ * Accessing blocker info
536
+ * ```ts
537
+ * const blockers: BlockerInfo[] = store.getState().getBlockingInfo('form');
538
+ * blockers.forEach(blocker => {
539
+ * console.log(`${blocker.id}: ${blocker.reason} (priority: ${blocker.priority})`);
540
+ * });
541
+ * ```
542
+ *
543
+ * @public
544
+ * @since 0.6.0
111
545
  */
112
546
  interface BlockerInfo extends StoredBlocker {
113
547
  id: string;
114
548
  }
115
549
  /**
116
- * Internal store state
550
+ * Internal state structure of the UI blocking store.
551
+ *
552
+ * Contains the core data structures for managing blockers and middleware.
553
+ * This state is combined with {@link UIBlockingStoreActions} to create the
554
+ * complete {@link UIBlockingStore} type.
555
+ *
556
+ * @internal
557
+ * @since 0.6.0
117
558
  */
118
559
  interface UIBlockingStoreState {
119
560
  activeBlockers: Map<string, StoredBlocker>;
120
561
  middlewares: Map<string, Middleware>;
121
562
  }
122
563
  /**
123
- * Store actions
564
+ * All available actions for managing UI blocking state.
565
+ *
566
+ * These methods allow adding, removing, updating blockers, checking blocking status,
567
+ * and managing middleware. Combined with {@link UIBlockingStoreState} to form
568
+ * the complete {@link UIBlockingStore} type.
569
+ *
570
+ * All actions are safe to call multiple times and handle edge cases gracefully.
571
+ *
572
+ * @example
573
+ * Using store actions directly
574
+ * ```ts
575
+ * import { uiBlockingStoreApi } from '@okyrychenko-dev/react-action-guard';
576
+ *
577
+ * // Add a blocker
578
+ * uiBlockingStoreApi.getState().addBlocker('my-blocker', {
579
+ * scope: 'form',
580
+ * reason: 'Processing...'
581
+ * });
582
+ *
583
+ * // Check if blocked
584
+ * const isBlocked = uiBlockingStoreApi.getState().isBlocked('form');
585
+ *
586
+ * // Remove blocker
587
+ * uiBlockingStoreApi.getState().removeBlocker('my-blocker');
588
+ * ```
589
+ *
590
+ * @public
591
+ * @since 0.6.0
124
592
  */
125
593
  interface UIBlockingStoreActions {
126
594
  addBlocker: (id: string, config?: BlockerConfig) => void;
595
+ updateBlocker: (id: string, config?: Partial<BlockerConfig>) => void;
127
596
  removeBlocker: (id: string) => void;
128
597
  isBlocked: (scope?: string | ReadonlyArray<string>) => boolean;
129
598
  getBlockingInfo: (scope: string) => ReadonlyArray<BlockerInfo>;
@@ -134,7 +603,32 @@ interface UIBlockingStoreActions {
134
603
  runMiddlewares: (context: MiddlewareContext) => Promise<void>;
135
604
  }
136
605
  /**
137
- * Complete store type (state + actions)
606
+ * Complete UI blocking store type combining state and actions.
607
+ *
608
+ * This is the full store interface used by hooks like {@link useUIBlockingStore}
609
+ * and accessible via {@link uiBlockingStoreApi}. It combines all state properties
610
+ * from {@link UIBlockingStoreState} with all action methods from {@link UIBlockingStoreActions}.
611
+ *
612
+ * @example
613
+ * Using the complete store
614
+ * ```ts
615
+ * import { useUIBlockingStore } from '@okyrychenko-dev/react-action-guard';
616
+ *
617
+ * function MyComponent() {
618
+ * const store = useUIBlockingStore();
619
+ *
620
+ * // Access state
621
+ * console.log(store.activeBlockers.size);
622
+ *
623
+ * // Call actions
624
+ * store.addBlocker('my-blocker', { scope: 'form' });
625
+ *
626
+ * return <div>...</div>;
627
+ * }
628
+ * ```
629
+ *
630
+ * @public
631
+ * @since 0.6.0
138
632
  */
139
633
  type UIBlockingStore = UIBlockingStoreState & UIBlockingStoreActions;
140
634
 
@@ -158,35 +652,157 @@ declare const useUIBlockingStore: {
158
652
  declare const uiBlockingStoreApi: zustand.StoreApi<UIBlockingStore>;
159
653
 
160
654
  /**
161
- * Hook to automatically add and remove a blocker based on component lifecycle
655
+ * Automatically manages a UI blocker based on component lifecycle.
656
+ *
657
+ * Creates a blocker that is automatically added when the condition is true
658
+ * and removed when the component unmounts or condition becomes false.
659
+ * Supports scoped blocking, priorities, timeouts, and works with both
660
+ * global store and isolated provider instances.
162
661
  *
163
- * Automatically adds a blocker when the component mounts (or when activated)
164
- * and removes it when the component unmounts (or when deactivated).
662
+ * @param blockerId - Unique identifier for this blocker. Must be unique within the scope.
663
+ * @param config - Configuration object for the blocker
664
+ * @param config.scope - Scope(s) to block. Can be a single scope string (e.g., "form", "navigation")
665
+ * or an array of scopes (e.g., ["form", "navigation"]). Defaults to "global".
666
+ * @param config.reason - Human-readable reason for blocking, displayed in DevTools and UI
667
+ * @param config.priority - Priority level (0-100). Higher priority blockers take precedence.
668
+ * Use 0-30 for low priority, 31-70 for medium, 71-100 for high priority.
669
+ * @param config.timeout - Auto-removal timeout in milliseconds. Blocker will be automatically
670
+ * removed after this duration. Use 0 or undefined for no timeout.
671
+ * @param config.onTimeout - Callback function invoked when the blocker times out
672
+ * @param isActive - When true, blocker is active; when false, blocker is removed.
673
+ * Defaults to true. Use this for conditional blocking.
165
674
  *
166
- * Supports both global store and context store (via UIBlockingProvider).
675
+ * @example
676
+ * Basic form submission blocking
677
+ * ```tsx
678
+ * function MyForm() {
679
+ * const [isSubmitting, setIsSubmitting] = useState(false);
680
+ *
681
+ * useBlocker('form-submit', {
682
+ * scope: 'form',
683
+ * reason: 'Submitting form...',
684
+ * priority: 50,
685
+ * timeout: 30000, // Auto-remove after 30 seconds
686
+ * onTimeout: () => {
687
+ * console.warn('Form submission timed out');
688
+ * setIsSubmitting(false);
689
+ * }
690
+ * }, isSubmitting);
691
+ *
692
+ * return <form onSubmit={handleSubmit}>...</form>;
693
+ * }
694
+ * ```
695
+ *
696
+ * @example
697
+ * Multiple scopes with high priority
698
+ * ```tsx
699
+ * useBlocker('critical-save', {
700
+ * scope: ['form', 'navigation', 'actions'],
701
+ * reason: 'Saving critical data...',
702
+ * priority: 90,
703
+ * }, isSaving);
704
+ * ```
705
+ *
706
+ * @example
707
+ * Conditional blocking based on unsaved changes
708
+ * ```tsx
709
+ * function Editor() {
710
+ * const [hasUnsavedChanges, setHasUnsavedChanges] = useState(false);
711
+ *
712
+ * useBlocker('unsaved-changes', {
713
+ * scope: 'navigation',
714
+ * reason: 'You have unsaved changes',
715
+ * priority: 80,
716
+ * }, hasUnsavedChanges);
717
+ *
718
+ * return <div>...</div>;
719
+ * }
720
+ * ```
167
721
  *
168
- * @param blockerId - Unique identifier for the blocker
169
- * @param config - Blocker configuration (scope, reason, priority)
170
- * @param isActive - Whether the blocker is currently active (default: true)
722
+ * @see {@link useIsBlocked} to check if a scope is currently blocked
723
+ * @see {@link useBlockingInfo} to get detailed blocker information
724
+ * @see {@link useAsyncAction} for async operation wrapping with automatic blocking
725
+ * @see {@link useConfirmableBlocker} for blockers that require user confirmation
171
726
  *
727
+ * @public
728
+ * @since 0.6.0
172
729
  */
173
730
  declare const useBlocker: (blockerId: string, config: BlockerConfig, isActive?: boolean) => void;
174
731
 
175
732
  /**
176
- * Hook to get detailed blocking information for a scope
733
+ * Gets detailed information about all active blockers for a specific scope.
177
734
  *
178
- * Returns an array of blocker information objects for the specified scope,
179
- * sorted by priority (highest first). Automatically re-renders when blockers change.
735
+ * Returns an array of blocker information objects, sorted by priority (highest first).
736
+ * Each blocker object contains id, reason, priority, and scope information.
737
+ * The hook automatically re-renders when blockers are added, removed, or updated.
180
738
  *
181
- * Uses a stable reference - only re-renders when the actual blocker list changes,
182
- * not on every store update.
739
+ * Uses memoization to provide a stable reference - only re-renders when the actual
740
+ * blocker list changes, not on every store update. This makes it efficient for
741
+ * rendering lists of blockers.
183
742
  *
184
- * Supports both global store and context store (via UIBlockingProvider).
743
+ * Works with both the global store and isolated provider instances (via UIBlockingProvider).
185
744
  *
186
- * @param scope - Scope to get blocking information for (default: "global")
187
- * @returns Array of blocker information objects, sorted by priority
745
+ * @param scope - Scope to get blocking information for. Defaults to "global" if not specified.
746
+ * @returns Array of {@link BlockerInfo} objects, sorted by priority (highest to lowest)
188
747
  *
748
+ * @example
749
+ * Display list of active blockers
750
+ * ```tsx
751
+ * function BlockerList() {
752
+ * const blockers = useBlockingInfo('form');
753
+ *
754
+ * if (blockers.length === 0) {
755
+ * return <div>No active blockers</div>;
756
+ * }
757
+ *
758
+ * return (
759
+ * <ul>
760
+ * {blockers.map((blocker) => (
761
+ * <li key={blocker.id}>
762
+ * {blocker.reason} (Priority: {blocker.priority})
763
+ * </li>
764
+ * ))}
765
+ * </ul>
766
+ * );
767
+ * }
768
+ * ```
769
+ *
770
+ * @example
771
+ * Show highest priority blocker reason
772
+ * ```tsx
773
+ * function BlockingBanner() {
774
+ * const blockers = useBlockingInfo('global');
775
+ *
776
+ * // blockers are already sorted by priority (highest first)
777
+ * const topBlocker = blockers[0];
778
+ *
779
+ * if (!topBlocker) {
780
+ * return null;
781
+ * }
782
+ *
783
+ * return <div className="banner">{topBlocker.reason}</div>;
784
+ * }
189
785
  * ```
786
+ *
787
+ * @example
788
+ * Count active blockers
789
+ * ```tsx
790
+ * function BlockerCount() {
791
+ * const formBlockers = useBlockingInfo('form');
792
+ * const navBlockers = useBlockingInfo('navigation');
793
+ *
794
+ * const total = formBlockers.length + navBlockers.length;
795
+ *
796
+ * return <div>{total} active blockers</div>;
797
+ * }
798
+ * ```
799
+ *
800
+ * @see {@link useIsBlocked} for a simple boolean check
801
+ * @see {@link useBlocker} to create blockers
802
+ * @see {@link BlockerInfo} for the structure of blocker information objects
803
+ *
804
+ * @public
805
+ * @since 0.6.0
190
806
  */
191
807
  declare const useBlockingInfo: (scope?: string) => ReadonlyArray<BlockerInfo>;
192
808
 
@@ -208,16 +824,150 @@ interface ConditionalBlockerConfig<TState = unknown> extends Omit<BlockerConfig,
208
824
  }
209
825
 
210
826
  /**
211
- * Hook for conditional blocking based on a condition function.
827
+ * Blocks UI based on a dynamic condition that is periodically evaluated.
828
+ *
829
+ * Periodically checks a condition function and automatically adds or removes
830
+ * a blocker based on the result. Useful for blocking based on application state,
831
+ * network status, feature flags, or any other dynamic condition.
212
832
  *
213
- * Periodically checks a condition and blocks/unblocks based on the result.
214
- * The condition is evaluated at the specified interval (default 1000ms).
833
+ * The condition function receives optional state and returns a boolean.
834
+ * It's evaluated immediately on mount and then at the specified interval.
835
+ * The blocker is added/removed automatically as the condition changes.
215
836
  *
216
- * Supports both global store and context store (via UIBlockingProvider).
837
+ * Works with both the global store and isolated provider instances (via UIBlockingProvider).
217
838
  *
218
- * @param blockerId - Unique identifier for this blocker
219
- * @param config - Configuration including condition function and check interval
839
+ * @template TState - Type of the state object passed to the condition function
840
+ * @param blockerId - Unique identifier for this conditional blocker
841
+ * @param config - Configuration for conditional blocking
842
+ * @param config.condition - Function that returns true to block, false to unblock.
843
+ * Receives the `state` object as parameter.
844
+ * @param config.state - Optional state object passed to the condition function
845
+ * @param config.checkInterval - How often to check the condition in milliseconds.
846
+ * Defaults to 1000ms (1 second). Lower values mean
847
+ * more responsive blocking but higher CPU usage.
848
+ * @param config.scope - Scope(s) to block when condition is true
849
+ * @param config.reason - Reason for blocking (displayed to users)
850
+ * @param config.priority - Optional blocker priority (0-100)
220
851
  *
852
+ * @example
853
+ * Block based on network status
854
+ * ```tsx
855
+ * function OfflineBlocker() {
856
+ * const [isOnline, setIsOnline] = useState(navigator.onLine);
857
+ *
858
+ * useEffect(() => {
859
+ * const handleOnline = () => setIsOnline(true);
860
+ * const handleOffline = () => setIsOnline(false);
861
+ *
862
+ * window.addEventListener('online', handleOnline);
863
+ * window.addEventListener('offline', handleOffline);
864
+ *
865
+ * return () => {
866
+ * window.removeEventListener('online', handleOnline);
867
+ * window.removeEventListener('offline', handleOffline);
868
+ * };
869
+ * }, []);
870
+ *
871
+ * useConditionalBlocker('offline', {
872
+ * condition: () => !isOnline,
873
+ * state: { isOnline },
874
+ * checkInterval: 500, // Check every 500ms
875
+ * scope: 'global',
876
+ * reason: 'No internet connection. Please check your network.',
877
+ * priority: 95
878
+ * });
879
+ *
880
+ * return <App />;
881
+ * }
882
+ * ```
883
+ *
884
+ * @example
885
+ * Block based on feature flag
886
+ * ```tsx
887
+ * function FeatureFlagBlocker() {
888
+ * const featureFlags = useFeatureFlags();
889
+ *
890
+ * useConditionalBlocker('feature-disabled', {
891
+ * condition: (state) => !state.featureFlags.newFeatureEnabled,
892
+ * state: { featureFlags },
893
+ * checkInterval: 5000, // Check every 5 seconds
894
+ * scope: 'new-feature',
895
+ * reason: 'This feature is currently disabled',
896
+ * priority: 70
897
+ * });
898
+ *
899
+ * return <NewFeature />;
900
+ * }
901
+ * ```
902
+ *
903
+ * @example
904
+ * Block during API rate limit
905
+ * ```tsx
906
+ * function RateLimitBlocker() {
907
+ * const [requestCount, setRequestCount] = useState(0);
908
+ * const [resetTime, setResetTime] = useState<number | null>(null);
909
+ *
910
+ * useConditionalBlocker('rate-limit', {
911
+ * condition: (state) => {
912
+ * if (!state.resetTime) {
913
+ * return false;
914
+ * }
915
+ * return Date.now() < state.resetTime && state.requestCount >= 100;
916
+ * },
917
+ * state: { requestCount, resetTime },
918
+ * checkInterval: 1000,
919
+ * scope: 'api',
920
+ * reason: 'API rate limit reached. Please wait.',
921
+ * priority: 90
922
+ * });
923
+ *
924
+ * return <Dashboard />;
925
+ * }
926
+ * ```
927
+ *
928
+ * @example
929
+ * Block based on form validation
930
+ * ```tsx
931
+ * function FormValidationBlocker() {
932
+ * const { formState } = useFormContext();
933
+ *
934
+ * useConditionalBlocker('invalid-form', {
935
+ * condition: (state) => !state.isValid && state.isDirty,
936
+ * state: formState,
937
+ * checkInterval: 500,
938
+ * scope: 'form-submit',
939
+ * reason: 'Please fix form errors before submitting',
940
+ * priority: 60
941
+ * });
942
+ *
943
+ * return <Form />;
944
+ * }
945
+ * ```
946
+ *
947
+ * @example
948
+ * Without state parameter (simple condition)
949
+ * ```tsx
950
+ * useConditionalBlocker('low-battery', {
951
+ * condition: () => {
952
+ * // Check battery level if available
953
+ * if ('getBattery' in navigator) {
954
+ * return navigator.getBattery().then(b => b.level < 0.05);
955
+ * }
956
+ * return false;
957
+ * },
958
+ * checkInterval: 30000, // Check every 30 seconds
959
+ * scope: 'heavy-operations',
960
+ * reason: 'Battery level too low for this operation',
961
+ * priority: 80
962
+ * });
963
+ * ```
964
+ *
965
+ * @see {@link useBlocker} for simple conditional blocking with boolean
966
+ * @see {@link useScheduledBlocker} for time-based blocking
967
+ * @see {@link ConditionalBlockerConfig} for configuration options
968
+ *
969
+ * @public
970
+ * @since 0.6.0
221
971
  */
222
972
  declare const useConditionalBlocker: <TState = unknown>(blockerId: string, config: ConditionalBlockerConfig<TState>) => void;
223
973
 
@@ -263,22 +1013,211 @@ interface UseConfirmableBlockerReturn {
263
1013
  }
264
1014
 
265
1015
  /**
266
- * Hook for creating a confirmable action with blocking UI
1016
+ * Creates a blocker that requires user confirmation before executing an action.
1017
+ *
1018
+ * Manages a confirmation dialog and blocking state for actions that need user approval.
1019
+ * The blocker is active during both the confirmation dialog display and action execution.
1020
+ *
1021
+ * Workflow:
1022
+ * 1. Call `execute()` to show confirmation dialog (blocker activated)
1023
+ * 2. User confirms → `onConfirm` callback runs (blocker remains active)
1024
+ * 3. Action completes → blocker removed
1025
+ * 4. User cancels → blocker removed, optional `onCancel` callback runs
1026
+ *
1027
+ * Works with both the global store and isolated provider instances (via UIBlockingProvider).
1028
+ *
1029
+ * @param blockerId - Unique identifier for this confirmable blocker
1030
+ * @param config - Configuration for the confirmable action
1031
+ * @param config.confirmMessage - Message to display in the confirmation dialog
1032
+ * @param config.confirmTitle - Optional dialog title (defaults to "Confirm Action")
1033
+ * @param config.confirmButtonText - Optional confirm button text (defaults to "Confirm")
1034
+ * @param config.cancelButtonText - Optional cancel button text (defaults to "Cancel")
1035
+ * @param config.onConfirm - Async function to execute after user confirms
1036
+ * @param config.onCancel - Optional callback when user cancels
1037
+ * @param config.scope - Scope(s) to block during confirmation and execution
1038
+ * @param config.reason - Optional custom blocking reason (defaults to confirmMessage)
1039
+ * @param config.priority - Optional blocker priority (0-100)
1040
+ *
1041
+ * @returns Object containing:
1042
+ * - `execute`: Function to trigger the confirmation dialog
1043
+ * - `isDialogOpen`: Boolean indicating if confirmation dialog is visible
1044
+ * - `isExecuting`: Boolean indicating if the confirmed action is executing
1045
+ * - `confirmConfig`: Dialog configuration (title, message, button texts)
1046
+ * - `onConfirm`: Function to call when user confirms
1047
+ * - `onCancel`: Function to call when user cancels
1048
+ *
1049
+ * @example
1050
+ * Delete confirmation with custom dialog
1051
+ * ```tsx
1052
+ * function DeleteButton({ itemId, itemName }) {
1053
+ * const confirmableDelete = useConfirmableBlocker('delete-item', {
1054
+ * confirmMessage: `Are you sure you want to delete "${itemName}"?`,
1055
+ * confirmTitle: 'Delete Item',
1056
+ * confirmButtonText: 'Delete',
1057
+ * cancelButtonText: 'Keep',
1058
+ * scope: ['form', 'navigation'],
1059
+ * priority: 80,
1060
+ * onConfirm: async () => {
1061
+ * await deleteItem(itemId);
1062
+ * showToast('Item deleted successfully');
1063
+ * },
1064
+ * onCancel: () => {
1065
+ * console.log('Delete cancelled');
1066
+ * }
1067
+ * });
267
1068
  *
1069
+ * return (
1070
+ * <>
1071
+ * <button onClick={confirmableDelete.execute}>
1072
+ * Delete
1073
+ * </button>
1074
+ *
1075
+ * {confirmableDelete.isDialogOpen && (
1076
+ * <ConfirmDialog
1077
+ * {...confirmableDelete.confirmConfig}
1078
+ * onConfirm={confirmableDelete.onConfirm}
1079
+ * onCancel={confirmableDelete.onCancel}
1080
+ * isLoading={confirmableDelete.isExecuting}
1081
+ * />
1082
+ * )}
1083
+ * </>
1084
+ * );
1085
+ * }
1086
+ * ```
1087
+ *
1088
+ * @example
1089
+ * Simple confirmation with default dialog
1090
+ * ```tsx
1091
+ * function LogoutButton() {
1092
+ * const confirmLogout = useConfirmableBlocker('logout', {
1093
+ * confirmMessage: 'Are you sure you want to log out?',
1094
+ * scope: 'global',
1095
+ * onConfirm: async () => {
1096
+ * await api.logout();
1097
+ * navigate('/login');
1098
+ * }
1099
+ * });
1100
+ *
1101
+ * return (
1102
+ * <>
1103
+ * <button onClick={confirmLogout.execute}>Logout</button>
1104
+ *
1105
+ * {confirmLogout.isDialogOpen && (
1106
+ * <Dialog
1107
+ * title={confirmLogout.confirmConfig.title}
1108
+ * message={confirmLogout.confirmConfig.message}
1109
+ * onConfirm={confirmLogout.onConfirm}
1110
+ * onCancel={confirmLogout.onCancel}
1111
+ * />
1112
+ * )}
1113
+ * </>
1114
+ * );
1115
+ * }
1116
+ * ```
1117
+ *
1118
+ * @example
1119
+ * With loading state during execution
1120
+ * ```tsx
1121
+ * function SaveButton() {
1122
+ * const confirmSave = useConfirmableBlocker('save-changes', {
1123
+ * confirmMessage: 'Save all changes?',
1124
+ * scope: 'form',
1125
+ * onConfirm: async () => {
1126
+ * await saveAllChanges();
1127
+ * }
1128
+ * });
1129
+ *
1130
+ * return (
1131
+ * <button
1132
+ * onClick={confirmSave.execute}
1133
+ * disabled={confirmSave.isExecuting}
1134
+ * >
1135
+ * {confirmSave.isExecuting ? 'Saving...' : 'Save All'}
1136
+ * </button>
1137
+ * );
1138
+ * }
1139
+ * ```
1140
+ *
1141
+ * @see {@link useBlocker} for simple blocking without confirmation
1142
+ * @see {@link ConfirmableBlockerConfig} for configuration options
1143
+ * @see {@link UseConfirmableBlockerReturn} for return value structure
1144
+ *
1145
+ * @public
1146
+ * @since 0.6.0
268
1147
  */
269
1148
  declare const useConfirmableBlocker: (blockerId: string, config: ConfirmableBlockerConfig) => UseConfirmableBlockerReturn;
270
1149
 
271
1150
  /**
272
- * Hook to check if a scope is currently blocked
1151
+ * Checks if one or more scopes are currently blocked.
1152
+ *
1153
+ * Returns a boolean indicating whether any of the specified scopes have active blockers.
1154
+ * The hook automatically re-renders the component when the blocking state changes,
1155
+ * ensuring your UI always reflects the current blocking state.
1156
+ *
1157
+ * Works with both the global store and isolated provider instances (via UIBlockingProvider).
1158
+ *
1159
+ * @param scope - Scope(s) to check for blocking. Can be:
1160
+ * - A single scope string (e.g., "form", "navigation", "global")
1161
+ * - An array of scopes (e.g., ["form", "navigation"])
1162
+ * - undefined to check the "global" scope
1163
+ * @returns true if ANY of the specified scopes are blocked, false otherwise
1164
+ *
1165
+ * @example
1166
+ * Basic single scope check
1167
+ * ```tsx
1168
+ * function SubmitButton() {
1169
+ * const isFormBlocked = useIsBlocked('form');
273
1170
  *
274
- * Returns a boolean indicating whether the specified scope(s) are blocked.
275
- * Automatically re-renders when the blocking state changes.
1171
+ * return (
1172
+ * <button disabled={isFormBlocked}>
1173
+ * {isFormBlocked ? 'Processing...' : 'Submit'}
1174
+ * </button>
1175
+ * );
1176
+ * }
1177
+ * ```
276
1178
  *
277
- * Supports both global store and context store (via UIBlockingProvider).
1179
+ * @example
1180
+ * Multiple scopes check
1181
+ * ```tsx
1182
+ * function NavigationLink({ href, children }) {
1183
+ * const isBlocked = useIsBlocked(['navigation', 'global']);
1184
+ *
1185
+ * return (
1186
+ * <a
1187
+ * href={href}
1188
+ * onClick={(e) => {
1189
+ * if (isBlocked) {
1190
+ * e.preventDefault();
1191
+ * alert('Navigation is currently blocked');
1192
+ * }
1193
+ * }}
1194
+ * style={{ opacity: isBlocked ? 0.5 : 1 }}
1195
+ * >
1196
+ * {children}
1197
+ * </a>
1198
+ * );
1199
+ * }
1200
+ * ```
278
1201
  *
279
- * @param scope - Scope or array of scopes to check (default: "global")
280
- * @returns True if any of the specified scopes are blocked
1202
+ * @example
1203
+ * Conditional rendering based on blocking state
1204
+ * ```tsx
1205
+ * function SaveIndicator() {
1206
+ * const isSaving = useIsBlocked('save');
1207
+ *
1208
+ * if (!isSaving) {
1209
+ * return null;
1210
+ * }
1211
+ *
1212
+ * return <div className="spinner">Saving...</div>;
1213
+ * }
1214
+ * ```
281
1215
  *
1216
+ * @see {@link useBlocker} to create a blocker
1217
+ * @see {@link useBlockingInfo} to get detailed information about active blockers
1218
+ *
1219
+ * @public
1220
+ * @since 0.6.0
282
1221
  */
283
1222
  declare const useIsBlocked: (scope?: string | ReadonlyArray<string>) => boolean;
284
1223
 
@@ -312,17 +1251,125 @@ interface ScheduledBlockerConfig extends BlockerConfig {
312
1251
  }
313
1252
 
314
1253
  /**
315
- * Hook for scheduling UI blocking for a specific time period.
1254
+ * Schedules UI blocking for a specific time period (e.g., maintenance windows).
1255
+ *
1256
+ * Automatically blocks UI between specified start and end times. Useful for
1257
+ * scheduled maintenance, planned downtime, or time-based feature restrictions.
1258
+ * Supports callbacks when the blocking period starts and ends.
1259
+ *
1260
+ * Time specifications support:
1261
+ * - Date objects
1262
+ * - ISO 8601 strings (e.g., "2024-01-15T10:00:00Z")
1263
+ * - Unix timestamps (milliseconds since epoch)
1264
+ *
1265
+ * You can specify the end time either as an absolute time or as a duration
1266
+ * from the start time. If both are provided, duration takes precedence.
1267
+ *
1268
+ * Works with both the global store and isolated provider instances (via UIBlockingProvider).
1269
+ *
1270
+ * @param blockerId - Unique identifier for this scheduled blocker
1271
+ * @param config - Schedule configuration
1272
+ * @param config.schedule - Schedule timing configuration
1273
+ * @param config.schedule.start - When to start blocking (Date, ISO string, or timestamp)
1274
+ * @param config.schedule.end - Optional absolute end time (Date, ISO string, or timestamp)
1275
+ * @param config.schedule.duration - Optional duration in milliseconds from start time
1276
+ * @param config.scope - Scope(s) to block during the scheduled period
1277
+ * @param config.reason - Reason for blocking (displayed to users)
1278
+ * @param config.priority - Optional blocker priority (0-100)
1279
+ * @param config.onScheduleStart - Optional callback when blocking period starts
1280
+ * @param config.onScheduleEnd - Optional callback when blocking period ends
1281
+ *
1282
+ * @example
1283
+ * Maintenance window with absolute times
1284
+ * ```tsx
1285
+ * function MaintenanceWarning() {
1286
+ * useScheduledBlocker('maintenance', {
1287
+ * schedule: {
1288
+ * start: new Date('2024-01-15T02:00:00Z'),
1289
+ * end: new Date('2024-01-15T04:00:00Z'),
1290
+ * },
1291
+ * scope: 'global',
1292
+ * reason: 'System maintenance in progress',
1293
+ * priority: 95,
1294
+ * onScheduleStart: () => {
1295
+ * console.log('Maintenance started');
1296
+ * showNotification('System is now in maintenance mode');
1297
+ * },
1298
+ * onScheduleEnd: () => {
1299
+ * console.log('Maintenance completed');
1300
+ * showNotification('System is back online');
1301
+ * }
1302
+ * });
316
1303
  *
317
- * Blocks UI during a scheduled maintenance window or time period.
318
- * Supports start/end times as Date objects, ISO strings, or timestamps.
319
- * If both duration and end are provided, duration takes precedence.
1304
+ * return <div>...</div>;
1305
+ * }
1306
+ * ```
320
1307
  *
321
- * Supports both global store and context store (via UIBlockingProvider).
1308
+ * @example
1309
+ * Short maintenance with duration
1310
+ * ```tsx
1311
+ * useScheduledBlocker('quick-maintenance', {
1312
+ * schedule: {
1313
+ * start: new Date('2024-01-15T03:00:00'),
1314
+ * duration: 15 * 60 * 1000, // 15 minutes
1315
+ * },
1316
+ * scope: ['api', 'forms'],
1317
+ * reason: 'Brief system update',
1318
+ * priority: 90
1319
+ * });
1320
+ * ```
1321
+ *
1322
+ * @example
1323
+ * Business hours enforcement
1324
+ * ```tsx
1325
+ * function BusinessHoursBlocker() {
1326
+ * const now = new Date();
1327
+ * const tomorrow9AM = new Date(now);
1328
+ * tomorrow9AM.setDate(now.getDate() + 1);
1329
+ * tomorrow9AM.setHours(9, 0, 0, 0);
322
1330
  *
323
- * @param blockerId - Unique identifier for this blocker
324
- * @param config - Configuration including schedule and callbacks
1331
+ * useScheduledBlocker('after-hours', {
1332
+ * schedule: {
1333
+ * start: new Date(now.getTime() + 1000), // Start immediately
1334
+ * end: tomorrow9AM,
1335
+ * },
1336
+ * scope: 'trading',
1337
+ * reason: 'Trading is only available during business hours (9 AM - 5 PM)',
1338
+ * priority: 85
1339
+ * });
325
1340
  *
1341
+ * return <TradingDashboard />;
1342
+ * }
1343
+ * ```
1344
+ *
1345
+ * @example
1346
+ * Using ISO strings for scheduling
1347
+ * ```tsx
1348
+ * useScheduledBlocker('deployment', {
1349
+ * schedule: {
1350
+ * start: '2024-01-20T00:00:00Z',
1351
+ * duration: 2 * 60 * 60 * 1000, // 2 hours
1352
+ * },
1353
+ * scope: 'global',
1354
+ * reason: 'Deploying new version',
1355
+ * priority: 100,
1356
+ * onScheduleStart: () => {
1357
+ * analytics.track('deployment_started');
1358
+ * },
1359
+ * onScheduleEnd: () => {
1360
+ * analytics.track('deployment_completed');
1361
+ * window.location.reload(); // Reload to get new version
1362
+ * }
1363
+ * });
1364
+ * ```
1365
+ *
1366
+ * @see {@link useBlocker} for immediate blocking without scheduling
1367
+ * @see {@link useConditionalBlocker} for condition-based blocking
1368
+ * @see {@link ScheduledBlockerConfig} for configuration options
1369
+ * @see {@link BlockingSchedule} for schedule specification details
1370
+ *
1371
+ * @public
1372
+ * @since 0.6.0
326
1373
  */
327
1374
  declare const useScheduledBlocker: (blockerId: string, config: ScheduledBlockerConfig) => void;
328
1375
 
@@ -342,7 +1389,7 @@ interface BlockerConfigTyped<TScope extends string> {
342
1389
  scope?: ScopeValue<TScope>;
343
1390
  /** Human-readable reason for blocking */
344
1391
  reason?: string;
345
- /** Priority level (higher priority blockers take precedence) */
1392
+ /** Priority level (higher priority blockers take precedence, minimum value is 0) */
346
1393
  priority?: number;
347
1394
  /** Timestamp when the blocker was created */
348
1395
  timestamp?: number;
@@ -395,7 +1442,14 @@ interface TypedHooks<TScope extends string> {
395
1442
  declare function createTypedHooks<TScope extends string>(): TypedHooks<TScope>;
396
1443
 
397
1444
  /**
398
- * Props for UIBlockingProvider component
1445
+ * Props for the {@link UIBlockingProvider} component.
1446
+ *
1447
+ * Configures an isolated UI blocking store instance for a subtree of your app.
1448
+ * Useful for SSR, testing, micro-frontends, or any scenario where you need
1449
+ * independent blocking state.
1450
+ *
1451
+ * @public
1452
+ * @since 0.6.0
399
1453
  */
400
1454
  interface UIBlockingProviderProps {
401
1455
  children: ReactNode;
@@ -407,8 +1461,8 @@ interface UIBlockingProviderProps {
407
1461
  middlewares?: ReadonlyArray<Middleware>;
408
1462
  }
409
1463
  /**
410
- * Base provider created using toolkit's createStoreProvider
411
- * This creates isolated store instances for SSR, testing, and micro-frontends
1464
+ * Base provider implementation created using toolkit's createStoreProvider.
1465
+ * @internal
412
1466
  */
413
1467
  declare const useUIBlockingContext: () => StoreApi<UIBlockingStore>;
414
1468
  declare const useUIBlockingStoreFromContext: {
@@ -417,10 +1471,100 @@ declare const useUIBlockingStoreFromContext: {
417
1471
  };
418
1472
  declare const useIsInsideUIBlockingProvider: () => boolean;
419
1473
  /**
420
- * Provider component for isolated UI blocking state
1474
+ * Provider component for isolated UI blocking state management.
1475
+ *
1476
+ * Creates an independent store instance for a subtree of your application.
1477
+ * Each provider has its own isolated blocking state, separate from the global
1478
+ * store and other provider instances.
1479
+ *
1480
+ * **Use cases:**
1481
+ * - Server-Side Rendering (SSR) - Avoid state sharing between requests
1482
+ * - Testing - Isolated state for each test
1483
+ * - Micro-frontends - Independent blocking state per micro-app
1484
+ * - Modals/Dialogs - Scoped blocking within a dialog
1485
+ *
1486
+ * Uses `createStoreProvider` from `@okyrychenko-dev/react-zustand-toolkit` with
1487
+ * built-in DevTools support and middleware registration.
1488
+ *
1489
+ * @param props - Provider configuration
1490
+ * @param props.children - React children to wrap with the provider
1491
+ * @param props.enableDevtools - Enable Redux DevTools integration. Defaults to true in development, false in production.
1492
+ * @param props.devtoolsName - Name displayed in Redux DevTools. Defaults to "UIBlocking".
1493
+ * @param props.middlewares - Array of middleware functions to register on store creation
1494
+ *
1495
+ * @example
1496
+ * Basic SSR setup
1497
+ * ```tsx
1498
+ * import { UIBlockingProvider } from '@okyrychenko-dev/react-action-guard';
1499
+ *
1500
+ * function App() {
1501
+ * return (
1502
+ * <UIBlockingProvider>
1503
+ * <MyApplication />
1504
+ * </UIBlockingProvider>
1505
+ * );
1506
+ * }
1507
+ * ```
1508
+ *
1509
+ * @example
1510
+ * With middleware and custom DevTools name
1511
+ * ```tsx
1512
+ * import { UIBlockingProvider, loggerMiddleware } from '@okyrychenko-dev/react-action-guard';
1513
+ *
1514
+ * function App() {
1515
+ * return (
1516
+ * <UIBlockingProvider
1517
+ * enableDevtools={true}
1518
+ * devtoolsName="MyApp-UIBlocking"
1519
+ * middlewares={[loggerMiddleware]}
1520
+ * >
1521
+ * <MyApplication />
1522
+ * </UIBlockingProvider>
1523
+ * );
1524
+ * }
1525
+ * ```
1526
+ *
1527
+ * @example
1528
+ * Multiple providers for micro-frontends
1529
+ * ```tsx
1530
+ * function MicroFrontendApp() {
1531
+ * return (
1532
+ * <div>
1533
+ * <UIBlockingProvider devtoolsName="MicroApp1">
1534
+ * <MicroApp1 />
1535
+ * </UIBlockingProvider>
1536
+ *
1537
+ * <UIBlockingProvider devtoolsName="MicroApp2">
1538
+ * <MicroApp2 />
1539
+ * </UIBlockingProvider>
1540
+ * </div>
1541
+ * );
1542
+ * }
1543
+ * ```
1544
+ *
1545
+ * @example
1546
+ * Testing with isolated state
1547
+ * ```tsx
1548
+ * // In your test file
1549
+ * import { render } from '@testing-library/react';
1550
+ * import { UIBlockingProvider } from '@okyrychenko-dev/react-action-guard';
1551
+ *
1552
+ * test('blocker behavior', () => {
1553
+ * render(
1554
+ * <UIBlockingProvider enableDevtools={false}>
1555
+ * <ComponentUnderTest />
1556
+ * </UIBlockingProvider>
1557
+ * );
1558
+ * // Each test gets isolated state
1559
+ * });
1560
+ * ```
1561
+ *
1562
+ * @see {@link useUIBlockingContext} to access the store from context
1563
+ * @see {@link useIsInsideUIBlockingProvider} to check if inside a provider
1564
+ * @see {@link UIBlockingProviderProps} for prop details
421
1565
  *
422
- * Uses toolkit's createStoreProvider with built-in DevTools support
423
- * and onStoreCreate callback for middleware registration.
1566
+ * @public
1567
+ * @since 0.6.0
424
1568
  */
425
1569
  declare function UIBlockingProvider({ children, enableDevtools, devtoolsName, middlewares, }: UIBlockingProviderProps): ReactNode;
426
1570