@okyrychenko-dev/react-action-guard 0.6.0 → 0.7.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/dist/index.d.ts CHANGED
@@ -13,21 +13,133 @@ 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
- declare const useAsyncAction: <T = unknown>(actionId: string, scope?: string | ReadonlyArray<string>, options?: UseAsyncActionOptions) => ((asyncFn: () => Promise<T>) => Promise<T>);
142
+ declare function useAsyncAction<T = unknown>(actionId: string, scope?: string | ReadonlyArray<string>, options?: UseAsyncActionOptions): (asyncFn: () => Promise<T>) => Promise<T>;
31
143
 
32
144
  type AnalyticsEventData = Record<string, string | number | ReadonlyArray<string> | undefined>;
33
145
  type AnalyticsProvider = "ga" | "mixpanel" | "amplitude";
@@ -58,30 +170,324 @@ interface MiddlewareContext {
58
170
  }
59
171
  type Middleware = (context: MiddlewareContext) => void | Promise<void>;
60
172
 
61
- declare const createAnalyticsMiddleware: (config: AnalyticsConfig) => Middleware;
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
+ */
246
+ declare function createAnalyticsMiddleware(config: AnalyticsConfig): Middleware;
62
247
 
63
248
  /**
64
- * 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.
65
257
  *
66
- * This is a convenience function that registers multiple middleware at once.
67
- * 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.)
68
260
  *
69
- * @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';
70
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
71
307
  */
72
308
  declare function configureMiddleware(middlewares: ReadonlyArray<Middleware>): void;
73
309
 
74
- declare const loggerMiddleware: Middleware;
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
+ */
362
+ declare function loggerMiddleware(context: MiddlewareContext): void;
75
363
 
76
364
  interface PerformanceConfig {
77
365
  slowBlockThreshold?: number;
78
366
  onSlowBlock?: (blockerId: string, duration: number) => void;
79
367
  }
80
368
 
81
- declare const createPerformanceMiddleware: (config?: PerformanceConfig) => Middleware;
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
+ */
454
+ declare function createPerformanceMiddleware(config?: PerformanceConfig): Middleware;
82
455
 
83
456
  /**
84
- * 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
85
491
  */
86
492
  interface BlockerConfig {
87
493
  scope?: string | ReadonlyArray<string>;
@@ -95,7 +501,16 @@ interface BlockerConfig {
95
501
  onTimeout?: (blockerId: string) => void;
96
502
  }
97
503
  /**
98
- * 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
99
514
  */
100
515
  interface StoredBlocker {
101
516
  scope: string | ReadonlyArray<string>;
@@ -110,20 +525,70 @@ interface StoredBlocker {
110
525
  onTimeout?: (blockerId: string) => void;
111
526
  }
112
527
  /**
113
- * 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
114
545
  */
115
546
  interface BlockerInfo extends StoredBlocker {
116
547
  id: string;
117
548
  }
118
549
  /**
119
- * 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
120
558
  */
121
559
  interface UIBlockingStoreState {
122
560
  activeBlockers: Map<string, StoredBlocker>;
123
561
  middlewares: Map<string, Middleware>;
124
562
  }
125
563
  /**
126
- * 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
127
592
  */
128
593
  interface UIBlockingStoreActions {
129
594
  addBlocker: (id: string, config?: BlockerConfig) => void;
@@ -138,7 +603,32 @@ interface UIBlockingStoreActions {
138
603
  runMiddlewares: (context: MiddlewareContext) => Promise<void>;
139
604
  }
140
605
  /**
141
- * 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
142
632
  */
143
633
  type UIBlockingStore = UIBlockingStoreState & UIBlockingStoreActions;
144
634
 
@@ -162,37 +652,159 @@ declare const useUIBlockingStore: {
162
652
  declare const uiBlockingStoreApi: zustand.StoreApi<UIBlockingStore>;
163
653
 
164
654
  /**
165
- * 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.
166
661
  *
167
- * Automatically adds a blocker when the component mounts (or when activated)
168
- * 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.
169
674
  *
170
- * 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
+ * ```
171
721
  *
172
- * @param blockerId - Unique identifier for the blocker
173
- * @param config - Blocker configuration (scope, reason, priority)
174
- * @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
175
726
  *
727
+ * @public
728
+ * @since 0.6.0
176
729
  */
177
- declare const useBlocker: (blockerId: string, config: BlockerConfig, isActive?: boolean) => void;
730
+ declare function useBlocker(blockerId: string, config: BlockerConfig, isActive?: boolean): void;
178
731
 
179
732
  /**
180
- * Hook to get detailed blocking information for a scope
733
+ * Gets detailed information about all active blockers for a specific scope.
181
734
  *
182
- * Returns an array of blocker information objects for the specified scope,
183
- * 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.
184
738
  *
185
- * Uses a stable reference - only re-renders when the actual blocker list changes,
186
- * 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.
187
742
  *
188
- * Supports both global store and context store (via UIBlockingProvider).
743
+ * Works with both the global store and isolated provider instances (via UIBlockingProvider).
189
744
  *
190
- * @param scope - Scope to get blocking information for (default: "global")
191
- * @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)
192
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
+ * }
193
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
194
806
  */
195
- declare const useBlockingInfo: (scope?: string) => ReadonlyArray<BlockerInfo>;
807
+ declare function useBlockingInfo(scope?: string): ReadonlyArray<BlockerInfo>;
196
808
 
197
809
  /**
198
810
  * Configuration for conditional blocker hook.
@@ -212,18 +824,152 @@ interface ConditionalBlockerConfig<TState = unknown> extends Omit<BlockerConfig,
212
824
  }
213
825
 
214
826
  /**
215
- * 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.
216
832
  *
217
- * Periodically checks a condition and blocks/unblocks based on the result.
218
- * 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.
219
836
  *
220
- * Supports both global store and context store (via UIBlockingProvider).
837
+ * Works with both the global store and isolated provider instances (via UIBlockingProvider).
221
838
  *
222
- * @param blockerId - Unique identifier for this blocker
223
- * @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)
224
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
225
971
  */
226
- declare const useConditionalBlocker: <TState = unknown>(blockerId: string, config: ConditionalBlockerConfig<TState>) => void;
972
+ declare function useConditionalBlocker<TState = unknown>(blockerId: string, config: ConditionalBlockerConfig<TState>): void;
227
973
 
228
974
  /**
229
975
  * Configuration for the confirmation dialog
@@ -267,24 +1013,213 @@ interface UseConfirmableBlockerReturn {
267
1013
  }
268
1014
 
269
1015
  /**
270
- * 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
+ * });
271
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
272
1147
  */
273
- declare const useConfirmableBlocker: (blockerId: string, config: ConfirmableBlockerConfig) => UseConfirmableBlockerReturn;
1148
+ declare function useConfirmableBlocker(blockerId: string, config: ConfirmableBlockerConfig): UseConfirmableBlockerReturn;
274
1149
 
275
1150
  /**
276
- * 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');
277
1170
  *
278
- * Returns a boolean indicating whether the specified scope(s) are blocked.
279
- * Automatically re-renders when the blocking state changes.
1171
+ * return (
1172
+ * <button disabled={isFormBlocked}>
1173
+ * {isFormBlocked ? 'Processing...' : 'Submit'}
1174
+ * </button>
1175
+ * );
1176
+ * }
1177
+ * ```
280
1178
  *
281
- * 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
+ * ```
282
1201
  *
283
- * @param scope - Scope or array of scopes to check (default: "global")
284
- * @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
+ * ```
285
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
286
1221
  */
287
- declare const useIsBlocked: (scope?: string | ReadonlyArray<string>) => boolean;
1222
+ declare function useIsBlocked(scope?: string | ReadonlyArray<string>): boolean;
288
1223
 
289
1224
  /**
290
1225
  * Schedule configuration for time-based blocking.
@@ -316,19 +1251,127 @@ interface ScheduledBlockerConfig extends BlockerConfig {
316
1251
  }
317
1252
 
318
1253
  /**
319
- * 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
+ * });
320
1303
  *
321
- * Blocks UI during a scheduled maintenance window or time period.
322
- * Supports start/end times as Date objects, ISO strings, or timestamps.
323
- * If both duration and end are provided, duration takes precedence.
1304
+ * return <div>...</div>;
1305
+ * }
1306
+ * ```
324
1307
  *
325
- * 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);
326
1330
  *
327
- * @param blockerId - Unique identifier for this blocker
328
- * @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
+ * });
329
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
330
1373
  */
331
- declare const useScheduledBlocker: (blockerId: string, config: ScheduledBlockerConfig) => void;
1374
+ declare function useScheduledBlocker(blockerId: string, config: ScheduledBlockerConfig): void;
332
1375
 
333
1376
  /**
334
1377
  * Default scope type
@@ -399,7 +1442,14 @@ interface TypedHooks<TScope extends string> {
399
1442
  declare function createTypedHooks<TScope extends string>(): TypedHooks<TScope>;
400
1443
 
401
1444
  /**
402
- * 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
403
1453
  */
404
1454
  interface UIBlockingProviderProps {
405
1455
  children: ReactNode;
@@ -411,8 +1461,8 @@ interface UIBlockingProviderProps {
411
1461
  middlewares?: ReadonlyArray<Middleware>;
412
1462
  }
413
1463
  /**
414
- * Base provider created using toolkit's createStoreProvider
415
- * This creates isolated store instances for SSR, testing, and micro-frontends
1464
+ * Base provider implementation created using toolkit's createStoreProvider.
1465
+ * @internal
416
1466
  */
417
1467
  declare const useUIBlockingContext: () => StoreApi<UIBlockingStore>;
418
1468
  declare const useUIBlockingStoreFromContext: {
@@ -421,10 +1471,100 @@ declare const useUIBlockingStoreFromContext: {
421
1471
  };
422
1472
  declare const useIsInsideUIBlockingProvider: () => boolean;
423
1473
  /**
424
- * 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
425
1565
  *
426
- * Uses toolkit's createStoreProvider with built-in DevTools support
427
- * and onStoreCreate callback for middleware registration.
1566
+ * @public
1567
+ * @since 0.6.0
428
1568
  */
429
1569
  declare function UIBlockingProvider({ children, enableDevtools, devtoolsName, middlewares, }: UIBlockingProviderProps): ReactNode;
430
1570