@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.js CHANGED
@@ -23,16 +23,18 @@ function normalizeScopeToArray(scope) {
23
23
  }
24
24
 
25
25
  // src/store/uiBlockingStore.actions.ts
26
- var normalizePriority = (priority, fallback = DEFAULT_PRIORITY) => {
26
+ function normalizePriority(priority, fallback = DEFAULT_PRIORITY) {
27
27
  return priority !== void 0 ? Math.max(0, priority) : fallback;
28
- };
29
- var createMiddlewareContext = (action, blockerId, config, prevState) => ({
30
- action,
31
- blockerId,
32
- config,
33
- timestamp: Date.now(),
34
- ...prevState && { prevState }
35
- });
28
+ }
29
+ function createMiddlewareContext(action, blockerId, config, prevState) {
30
+ return {
31
+ action,
32
+ blockerId,
33
+ config,
34
+ timestamp: Date.now(),
35
+ ...prevState && { prevState }
36
+ };
37
+ }
36
38
  var createUIBlockingActions = (set, get) => ({
37
39
  // State
38
40
  activeBlockers: /* @__PURE__ */ new Map(),
@@ -63,11 +65,38 @@ var createUIBlockingActions = (set, get) => ({
63
65
  },
64
66
  // Actions
65
67
  /**
66
- * Add a blocker to the store
68
+ * Adds a new blocker or overwrites an existing one.
69
+ *
70
+ * If a blocker with the same ID already exists, it will be replaced and any
71
+ * existing timeout will be cleared. The blocker is active immediately after
72
+ * being added. Triggers middleware with 'add' action.
73
+ *
74
+ * @param id - Unique identifier for the blocker. Must be unique within the store.
75
+ * @param config - Blocker configuration. All fields are optional with defaults applied.
67
76
  *
68
- * @param id - Unique blocker identifier
69
- * @param config - Optional blocker configuration
77
+ * @example
78
+ * Basic usage
79
+ * ```ts
80
+ * store.getState().addBlocker('save-operation', {
81
+ * scope: 'form',
82
+ * reason: 'Saving data...',
83
+ * priority: 70
84
+ * });
85
+ * ```
70
86
  *
87
+ * @example
88
+ * With timeout
89
+ * ```ts
90
+ * store.getState().addBlocker('api-call', {
91
+ * scope: 'global',
92
+ * reason: 'Loading...',
93
+ * timeout: 30000, // Auto-remove after 30 seconds
94
+ * onTimeout: (id) => console.warn(`${id} timed out`)
95
+ * });
96
+ * ```
97
+ *
98
+ * @see {@link removeBlocker} to remove a blocker
99
+ * @see {@link updateBlocker} to update an existing blocker
71
100
  */
72
101
  addBlocker: (id, config = {}) => {
73
102
  const existingBlocker = get().activeBlockers.get(id);
@@ -115,11 +144,31 @@ var createUIBlockingActions = (set, get) => ({
115
144
  );
116
145
  },
117
146
  /**
118
- * Update blocker metadata. If timeout is updated, the timer will be restarted.
147
+ * Updates an existing blocker or creates it if it doesn't exist.
148
+ *
149
+ * Merges the provided configuration with existing blocker data. If the timeout
150
+ * is changed, the timeout timer is restarted. Triggers middleware with 'update' action.
151
+ *
152
+ * @param id - Identifier of the blocker to update
153
+ * @param config - Partial configuration to merge with existing blocker data
119
154
  *
120
- * @param id - Unique blocker identifier
121
- * @param config - Partial blocker configuration to merge
155
+ * @example
156
+ * Update blocker reason
157
+ * ```ts
158
+ * store.getState().updateBlocker('save', {
159
+ * reason: 'Saving additional data...'
160
+ * });
161
+ * ```
122
162
  *
163
+ * @example
164
+ * Extend timeout
165
+ * ```ts
166
+ * store.getState().updateBlocker('api-call', {
167
+ * timeout: 60000 // Extend to 60 seconds
168
+ * });
169
+ * ```
170
+ *
171
+ * @see {@link addBlocker} to create a new blocker
123
172
  */
124
173
  updateBlocker: (id, config = {}) => {
125
174
  const existingBlocker = get().activeBlockers.get(id);
@@ -193,10 +242,23 @@ var createUIBlockingActions = (set, get) => ({
193
242
  );
194
243
  },
195
244
  /**
196
- * Remove a blocker from the store
245
+ * Removes a blocker from the store.
246
+ *
247
+ * Clears any associated timeout and triggers middleware with 'remove' action.
248
+ * Safe to call even if the blocker doesn't exist (no-op in that case).
197
249
  *
198
- * @param id - Blocker identifier to remove
250
+ * @param id - Identifier of the blocker to remove
199
251
  *
252
+ * @example
253
+ * ```ts
254
+ * // Add and remove a blocker
255
+ * store.getState().addBlocker('temp', { scope: 'form' });
256
+ * // ... later
257
+ * store.getState().removeBlocker('temp');
258
+ * ```
259
+ *
260
+ * @see {@link addBlocker} to add a blocker
261
+ * @see {@link clearAllBlockers} to remove all blockers at once
200
262
  */
201
263
  removeBlocker: (id) => {
202
264
  const prevBlocker = get().activeBlockers.get(id);
@@ -213,11 +275,30 @@ var createUIBlockingActions = (set, get) => ({
213
275
  }
214
276
  },
215
277
  /**
216
- * Check if a scope is currently blocked
278
+ * Checks if one or more scopes are currently blocked.
279
+ *
280
+ * Returns true if ANY blocker affects the specified scope(s). Global scope
281
+ * blockers will cause this to return true for any scope check.
282
+ *
283
+ * @param scope - Scope(s) to check. Defaults to "global" if not specified.
284
+ * Can be a single scope string or array of scopes.
285
+ * @returns true if any of the specified scopes are currently blocked
286
+ *
287
+ * @example
288
+ * Single scope check
289
+ * ```ts
290
+ * const isFormBlocked = store.getState().isBlocked('form');
291
+ * console.log(isFormBlocked); // true or false
292
+ * ```
217
293
  *
218
- * @param scope - Scope or array of scopes to check (default: "global")
219
- * @returns True if any of the scopes are blocked
294
+ * @example
295
+ * Multiple scopes check
296
+ * ```ts
297
+ * const isBlocked = store.getState().isBlocked(['form', 'navigation']);
298
+ * // Returns true if either 'form' OR 'navigation' has active blockers
299
+ * ```
220
300
  *
301
+ * @see {@link getBlockingInfo} for detailed blocker information
221
302
  */
222
303
  isBlocked: (scope = DEFAULT_SCOPE) => {
223
304
  const { activeBlockers } = get();
@@ -234,11 +315,37 @@ var createUIBlockingActions = (set, get) => ({
234
315
  return false;
235
316
  },
236
317
  /**
237
- * Get detailed blocking information for a specific scope
318
+ * Gets detailed information about all blockers affecting a specific scope.
319
+ *
320
+ * Returns an array of blocker objects sorted by priority (highest first).
321
+ * Includes global blockers and scope-specific blockers. Each blocker object
322
+ * contains id, reason, priority, scope, timestamp, and timeout information.
238
323
  *
239
324
  * @param scope - Scope to get blocking information for
240
- * @returns Array of blockers affecting the scope, sorted by priority (highest first)
325
+ * @returns Array of {@link BlockerInfo} objects, sorted by priority descending
326
+ *
327
+ * @example
328
+ * Display blocker details
329
+ * ```ts
330
+ * const blockers = store.getState().getBlockingInfo('form');
331
+ * blockers.forEach(blocker => {
332
+ * console.log(`${blocker.id}: ${blocker.reason}`);
333
+ * console.log(`Priority: ${blocker.priority}`);
334
+ * });
335
+ * ```
336
+ *
337
+ * @example
338
+ * Get highest priority blocker
339
+ * ```ts
340
+ * const blockers = store.getState().getBlockingInfo('navigation');
341
+ * const topBlocker = blockers[0]; // Highest priority
342
+ * if (topBlocker) {
343
+ * console.log(`Top blocker: ${topBlocker.reason}`);
344
+ * }
345
+ * ```
241
346
  *
347
+ * @see {@link isBlocked} for a simple boolean check
348
+ * @see {@link BlockerInfo} for the structure of returned objects
242
349
  */
243
350
  getBlockingInfo: (scope) => {
244
351
  const { activeBlockers } = get();
@@ -252,7 +359,18 @@ var createUIBlockingActions = (set, get) => ({
252
359
  return blockers.sort((a, b) => b.priority - a.priority);
253
360
  },
254
361
  /**
255
- * Clear all blockers from the store
362
+ * Removes all active blockers from the store.
363
+ *
364
+ * Clears all timeouts and triggers middleware with 'clear' action.
365
+ * Useful for resetting the blocking state.
366
+ *
367
+ * @example
368
+ * ```ts
369
+ * // Clear all blockers on logout
370
+ * store.getState().clearAllBlockers();
371
+ * ```
372
+ *
373
+ * @see {@link clearBlockersForScope} to clear blockers for a specific scope
256
374
  */
257
375
  clearAllBlockers: () => {
258
376
  const { activeBlockers } = get();
@@ -273,10 +391,28 @@ var createUIBlockingActions = (set, get) => ({
273
391
  }
274
392
  },
275
393
  /**
276
- * Clear all blockers for a specific scope
394
+ * Removes all blockers that affect a specific scope.
395
+ *
396
+ * Clears timeouts for removed blockers and triggers middleware with
397
+ * 'clear_scope' action. Global scope blockers are NOT cleared by this method.
277
398
  *
278
399
  * @param scope - Scope to clear blockers for
279
400
  *
401
+ * @example
402
+ * Clear form blockers
403
+ * ```ts
404
+ * // Clear all blockers affecting 'form' scope
405
+ * store.getState().clearBlockersForScope('form');
406
+ * ```
407
+ *
408
+ * @example
409
+ * Clear on navigation
410
+ * ```ts
411
+ * // Clear navigation blockers when user navigates away
412
+ * store.getState().clearBlockersForScope('navigation');
413
+ * ```
414
+ *
415
+ * @see {@link clearAllBlockers} to clear all blockers
280
416
  */
281
417
  clearBlockersForScope: (scope) => {
282
418
  const { activeBlockers } = get();
@@ -363,7 +499,7 @@ function useResolvedStoreWithSelector(selector) {
363
499
  }
364
500
 
365
501
  // src/hooks/useAsyncAction/useAsyncAction.ts
366
- var useAsyncAction = (actionId, scope, options) => {
502
+ function useAsyncAction(actionId, scope, options) {
367
503
  const { addBlocker, removeBlocker } = useResolvedStoreWithSelector((state) => ({
368
504
  addBlocker: state.addBlocker,
369
505
  removeBlocker: state.removeBlocker
@@ -389,8 +525,8 @@ var useAsyncAction = (actionId, scope, options) => {
389
525
  [actionId, scope, options?.timeout, options?.onTimeout, addBlocker, removeBlocker]
390
526
  );
391
527
  return executeWithBlocking;
392
- };
393
- var useBlocker = (blockerId, config, isActive = true) => {
528
+ }
529
+ function useBlocker(blockerId, config, isActive = true) {
394
530
  const { addBlocker, removeBlocker } = useResolvedStoreWithSelector((state) => ({
395
531
  addBlocker: state.addBlocker,
396
532
  removeBlocker: state.removeBlocker
@@ -404,7 +540,7 @@ var useBlocker = (blockerId, config, isActive = true) => {
404
540
  removeBlocker(blockerId);
405
541
  };
406
542
  }, [blockerId, isActive, addBlocker, removeBlocker]);
407
- };
543
+ }
408
544
 
409
545
  // src/hooks/useBlocker/useBlocker.utils.ts
410
546
  function createBlockerConfig(config) {
@@ -414,13 +550,13 @@ function createBlockerConfig(config) {
414
550
  priority: config.priority
415
551
  };
416
552
  }
417
- var useBlockingInfo = (scope = DEFAULT_SCOPE) => {
553
+ function useBlockingInfo(scope = DEFAULT_SCOPE) {
418
554
  const store = useResolvedStore();
419
555
  const activeBlockers = useStore(store, (state) => state.activeBlockers);
420
556
  return useMemo(() => {
421
557
  return store.getState().getBlockingInfo(scope);
422
558
  }, [activeBlockers, scope, store]);
423
- };
559
+ }
424
560
  function useConfigRef(config) {
425
561
  const configRef = useRef(config);
426
562
  useEffect(() => {
@@ -430,7 +566,7 @@ function useConfigRef(config) {
430
566
  }
431
567
 
432
568
  // src/hooks/useConditionalBlocker/useConditionalBlocker.ts
433
- var useConditionalBlocker = (blockerId, config) => {
569
+ function useConditionalBlocker(blockerId, config) {
434
570
  const { addBlocker, removeBlocker } = useResolvedStoreWithSelector((state) => ({
435
571
  addBlocker: state.addBlocker,
436
572
  removeBlocker: state.removeBlocker
@@ -466,8 +602,8 @@ var useConditionalBlocker = (blockerId, config) => {
466
602
  }
467
603
  };
468
604
  }, [checkCondition, blockerId, removeBlocker]);
469
- };
470
- var useConfirmableBlocker = (blockerId, config) => {
605
+ }
606
+ function useConfirmableBlocker(blockerId, config) {
471
607
  const [isDialogOpen, setIsDialogOpen] = useState(false);
472
608
  const [isExecuting, setIsExecuting] = useState(false);
473
609
  useBlocker(
@@ -510,12 +646,12 @@ var useConfirmableBlocker = (blockerId, config) => {
510
646
  onConfirm,
511
647
  onCancel
512
648
  };
513
- };
649
+ }
514
650
 
515
651
  // src/hooks/useIsBlocked/useIsBlocked.ts
516
- var useIsBlocked = (scope) => {
652
+ function useIsBlocked(scope) {
517
653
  return useResolvedStoreWithSelector((state) => state.isBlocked(scope));
518
- };
654
+ }
519
655
 
520
656
  // src/hooks/useScheduledBlocker/useScheduledBlocker.utils.ts
521
657
  var MAX_TIMEOUT_MS = 2147483647;
@@ -555,7 +691,7 @@ function isScheduleInPast(startTime, endTime, now) {
555
691
  }
556
692
 
557
693
  // src/hooks/useScheduledBlocker/useScheduledBlocker.ts
558
- var useScheduledBlocker = (blockerId, config) => {
694
+ function useScheduledBlocker(blockerId, config) {
559
695
  const { addBlocker, removeBlocker } = useResolvedStoreWithSelector((state) => ({
560
696
  addBlocker: state.addBlocker,
561
697
  removeBlocker: state.removeBlocker
@@ -621,13 +757,13 @@ var useScheduledBlocker = (blockerId, config) => {
621
757
  removeBlocker(blockerId);
622
758
  };
623
759
  }, [blockerId, addBlocker, removeBlocker, cleanup]);
624
- };
760
+ }
625
761
 
626
762
  // src/middleware/analyticsMiddleware.utils.ts
627
- var capitalizeFirst = (str) => {
763
+ function capitalizeFirst(str) {
628
764
  return str.charAt(0).toUpperCase() + str.slice(1);
629
- };
630
- var buildEventData = (context) => {
765
+ }
766
+ function buildEventData(context) {
631
767
  const eventData = {
632
768
  blocker_id: context.blockerId
633
769
  };
@@ -642,33 +778,33 @@ var buildEventData = (context) => {
642
778
  eventData.priority = priority;
643
779
  }
644
780
  return eventData;
645
- };
646
- var trackGoogleAnalytics = (action, eventData) => {
781
+ }
782
+ function trackGoogleAnalytics(action, eventData) {
647
783
  if (window.gtag) {
648
784
  window.gtag("event", `ui_blocking_${action}`, eventData);
649
785
  }
650
- };
651
- var trackMixpanel = (action, eventData) => {
786
+ }
787
+ function trackMixpanel(action, eventData) {
652
788
  if (window.mixpanel) {
653
789
  const eventName = `UI Blocking ${action.split("_").map(capitalizeFirst).join(" ")}`;
654
790
  window.mixpanel.track(eventName, eventData);
655
791
  }
656
- };
657
- var trackAmplitude = (action, eventData) => {
792
+ }
793
+ function trackAmplitude(action, eventData) {
658
794
  if (window.amplitude) {
659
795
  const eventName = `UI Blocking ${action.split("_").map(capitalizeFirst).join(" ")}`;
660
796
  window.amplitude.track(eventName, eventData);
661
797
  }
662
- };
663
- var trackCustom = (trackFn, action, eventData) => {
798
+ }
799
+ function trackCustom(trackFn, action, eventData) {
664
800
  try {
665
801
  trackFn(`ui_blocking_${action}`, eventData);
666
802
  } catch {
667
803
  }
668
- };
804
+ }
669
805
 
670
806
  // src/middleware/analyticsMiddleware.ts
671
- var trackEvent = (config, context, eventData) => {
807
+ function trackEvent(config, context, eventData) {
672
808
  if ("provider" in config) {
673
809
  switch (config.provider) {
674
810
  case "ga":
@@ -685,13 +821,13 @@ var trackEvent = (config, context, eventData) => {
685
821
  if ("track" in config) {
686
822
  trackCustom(config.track, context.action, eventData);
687
823
  }
688
- };
689
- var createAnalyticsMiddleware = (config) => {
824
+ }
825
+ function createAnalyticsMiddleware(config) {
690
826
  return (context) => {
691
827
  const eventData = buildEventData(context);
692
828
  trackEvent(config, context, eventData);
693
829
  };
694
- };
830
+ }
695
831
 
696
832
  // src/middleware/configureMiddleware.ts
697
833
  function configureMiddleware(middlewares) {
@@ -703,7 +839,7 @@ function configureMiddleware(middlewares) {
703
839
  }
704
840
 
705
841
  // src/middleware/loggerMiddleware.utils.ts
706
- var getActionEmoji = (action) => {
842
+ function getActionEmoji(action) {
707
843
  switch (action) {
708
844
  case "add":
709
845
  return "\u2795";
@@ -720,8 +856,8 @@ var getActionEmoji = (action) => {
720
856
  default:
721
857
  return "\u2753";
722
858
  }
723
- };
724
- var extractConfigDetails = (config) => {
859
+ }
860
+ function extractConfigDetails(config) {
725
861
  if (!config) {
726
862
  return {};
727
863
  }
@@ -734,8 +870,8 @@ var extractConfigDetails = (config) => {
734
870
  details.priority = priority;
735
871
  }
736
872
  return details;
737
- };
738
- var formatLogData = (context) => {
873
+ }
874
+ function formatLogData(context) {
739
875
  const configDetails = extractConfigDetails(context.config);
740
876
  const logData = {};
741
877
  if (context.scope !== void 0) {
@@ -754,26 +890,26 @@ var formatLogData = (context) => {
754
890
  return configDetails;
755
891
  }
756
892
  return Object.keys(logData).length === 1 && logData.config ? configDetails : logData;
757
- };
893
+ }
758
894
 
759
895
  // src/middleware/loggerMiddleware.ts
760
896
  var LOG_PREFIX = "[UIBlocking]";
761
- var loggerMiddleware = (context) => {
897
+ function loggerMiddleware(context) {
762
898
  const emoji = getActionEmoji(context.action);
763
899
  const logData = formatLogData(context);
764
900
  console.log(LOG_PREFIX, emoji, context.blockerId, logData);
765
- };
901
+ }
766
902
 
767
903
  // src/middleware/performanceMiddleware.utils.ts
768
- var handleAddAction = (blockerId, timestamp, blockStartTimes) => {
904
+ function handleAddAction(blockerId, timestamp, blockStartTimes) {
769
905
  blockStartTimes.set(blockerId, timestamp);
770
- };
906
+ }
771
907
 
772
908
  // src/middleware/performanceMiddleware.ts
773
909
  var DEFAULT_SLOW_BLOCK_THRESHOLD = 3e3;
774
910
  var ACTION_ADD = "add";
775
911
  var ACTION_REMOVE = "remove";
776
- var handleRemoveAction = (context, blockStartTimes, slowBlockThreshold, onSlowBlock) => {
912
+ function handleRemoveAction(context, blockStartTimes, slowBlockThreshold, onSlowBlock) {
777
913
  const startTime = blockStartTimes.get(context.blockerId);
778
914
  if (startTime === void 0) {
779
915
  return;
@@ -786,8 +922,8 @@ var handleRemoveAction = (context, blockStartTimes, slowBlockThreshold, onSlowBl
786
922
  );
787
923
  onSlowBlock?.(context.blockerId, duration);
788
924
  }
789
- };
790
- var createPerformanceMiddleware = (config = {}) => {
925
+ }
926
+ function createPerformanceMiddleware(config = {}) {
791
927
  const { slowBlockThreshold = DEFAULT_SLOW_BLOCK_THRESHOLD, onSlowBlock } = config;
792
928
  const blockStartTimes = /* @__PURE__ */ new Map();
793
929
  return (context) => {
@@ -797,7 +933,7 @@ var createPerformanceMiddleware = (config = {}) => {
797
933
  handleRemoveAction(context, blockStartTimes, slowBlockThreshold, onSlowBlock);
798
934
  }
799
935
  };
800
- };
936
+ }
801
937
 
802
938
  // src/createTypedHooks.ts
803
939
  function createTypedHooks() {