@okyrychenko-dev/react-action-guard 0.1.0 → 0.2.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/README.md +33 -37
- package/dist/index.cjs +361 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +162 -2
- package/dist/index.d.ts +162 -2
- package/dist/index.js +357 -2
- package/dist/index.js.map +1 -1
- package/package.json +5 -4
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useEffect, useCallback } from 'react';
|
|
1
|
+
import { useEffect, useCallback, useRef, useState } from 'react';
|
|
2
2
|
import { create } from 'zustand';
|
|
3
3
|
import { devtools } from 'zustand/middleware';
|
|
4
4
|
|
|
@@ -23,6 +23,33 @@ function normalizeScopeToArray(scope) {
|
|
|
23
23
|
var createUIBlockingActions = (set, get) => ({
|
|
24
24
|
// State
|
|
25
25
|
activeBlockers: /* @__PURE__ */ new Map(),
|
|
26
|
+
middlewares: /* @__PURE__ */ new Map(),
|
|
27
|
+
registerMiddleware: (name, middleware) => {
|
|
28
|
+
set((state) => {
|
|
29
|
+
const newMiddlewares = new Map(state.middlewares);
|
|
30
|
+
newMiddlewares.set(name, middleware);
|
|
31
|
+
return { middlewares: newMiddlewares };
|
|
32
|
+
});
|
|
33
|
+
console.log(`[UIBlocking] Registered middleware: ${name}`);
|
|
34
|
+
},
|
|
35
|
+
unregisterMiddleware: (name) => {
|
|
36
|
+
set((state) => {
|
|
37
|
+
const newMiddlewares = new Map(state.middlewares);
|
|
38
|
+
newMiddlewares.delete(name);
|
|
39
|
+
return { middlewares: newMiddlewares };
|
|
40
|
+
});
|
|
41
|
+
console.log(`[UIBlocking] Unregistered middleware: ${name}`);
|
|
42
|
+
},
|
|
43
|
+
runMiddlewares: async (context) => {
|
|
44
|
+
const { middlewares } = get();
|
|
45
|
+
for (const [name, middleware] of middlewares) {
|
|
46
|
+
try {
|
|
47
|
+
await middleware(context);
|
|
48
|
+
} catch (error) {
|
|
49
|
+
console.error(`[UIBlocking] Middleware "${name}" error:`, error);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
},
|
|
26
53
|
// Actions
|
|
27
54
|
/**
|
|
28
55
|
* Add a blocker to the store
|
|
@@ -43,6 +70,16 @@ var createUIBlockingActions = (set, get) => ({
|
|
|
43
70
|
newBlockers.set(id, storedBlocker);
|
|
44
71
|
return { activeBlockers: newBlockers };
|
|
45
72
|
});
|
|
73
|
+
void get().runMiddlewares({
|
|
74
|
+
action: "add",
|
|
75
|
+
blockerId: id,
|
|
76
|
+
config: {
|
|
77
|
+
scope: config.scope,
|
|
78
|
+
reason: config.reason,
|
|
79
|
+
priority: config.priority
|
|
80
|
+
},
|
|
81
|
+
timestamp: Date.now()
|
|
82
|
+
});
|
|
46
83
|
},
|
|
47
84
|
/**
|
|
48
85
|
* Remove a blocker from the store
|
|
@@ -51,11 +88,21 @@ var createUIBlockingActions = (set, get) => ({
|
|
|
51
88
|
*
|
|
52
89
|
*/
|
|
53
90
|
removeBlocker: (id) => {
|
|
91
|
+
const prevBlocker = get().activeBlockers.get(id);
|
|
54
92
|
set((state) => {
|
|
55
93
|
const newBlockers = new Map(state.activeBlockers);
|
|
56
94
|
newBlockers.delete(id);
|
|
57
95
|
return { activeBlockers: newBlockers };
|
|
58
96
|
});
|
|
97
|
+
if (prevBlocker) {
|
|
98
|
+
void get().runMiddlewares({
|
|
99
|
+
action: "remove",
|
|
100
|
+
blockerId: id,
|
|
101
|
+
config: prevBlocker,
|
|
102
|
+
timestamp: Date.now(),
|
|
103
|
+
prevState: prevBlocker
|
|
104
|
+
});
|
|
105
|
+
}
|
|
59
106
|
},
|
|
60
107
|
/**
|
|
61
108
|
* Check if a scope is currently blocked
|
|
@@ -171,7 +218,315 @@ var useAsyncAction = (actionId, scope) => {
|
|
|
171
218
|
);
|
|
172
219
|
return executeWithBlocking;
|
|
173
220
|
};
|
|
221
|
+
function useConfigRef(config) {
|
|
222
|
+
const configRef = useRef(config);
|
|
223
|
+
useEffect(() => {
|
|
224
|
+
configRef.current = config;
|
|
225
|
+
}, [config]);
|
|
226
|
+
return configRef;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// src/hooks/useBlocker.utils.ts
|
|
230
|
+
function createBlockerConfig(config) {
|
|
231
|
+
return {
|
|
232
|
+
scope: config.scope,
|
|
233
|
+
reason: config.reason,
|
|
234
|
+
priority: config.priority
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// src/hooks/useConditionalBlocker.ts
|
|
239
|
+
var useConditionalBlocker = (blockerId, config) => {
|
|
240
|
+
const { addBlocker, removeBlocker } = useUIBlockingStore();
|
|
241
|
+
const intervalRef = useRef(null);
|
|
242
|
+
const isBlockedRef = useRef(false);
|
|
243
|
+
const configRef = useConfigRef(config);
|
|
244
|
+
const checkCondition = useCallback(() => {
|
|
245
|
+
const currentConfig = configRef.current;
|
|
246
|
+
const shouldBlock = currentConfig.condition(currentConfig.state);
|
|
247
|
+
if (shouldBlock === isBlockedRef.current) {
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
if (shouldBlock) {
|
|
251
|
+
addBlocker(blockerId, createBlockerConfig(currentConfig));
|
|
252
|
+
isBlockedRef.current = true;
|
|
253
|
+
} else {
|
|
254
|
+
removeBlocker(blockerId);
|
|
255
|
+
isBlockedRef.current = false;
|
|
256
|
+
}
|
|
257
|
+
}, [blockerId, addBlocker, removeBlocker, configRef]);
|
|
258
|
+
useEffect(() => {
|
|
259
|
+
checkCondition();
|
|
260
|
+
const interval = configRef.current.checkInterval ?? 1e3;
|
|
261
|
+
intervalRef.current = setInterval(checkCondition, interval);
|
|
262
|
+
return () => {
|
|
263
|
+
if (intervalRef.current) {
|
|
264
|
+
clearInterval(intervalRef.current);
|
|
265
|
+
intervalRef.current = null;
|
|
266
|
+
}
|
|
267
|
+
if (isBlockedRef.current) {
|
|
268
|
+
removeBlocker(blockerId);
|
|
269
|
+
isBlockedRef.current = false;
|
|
270
|
+
}
|
|
271
|
+
};
|
|
272
|
+
}, [checkCondition, blockerId, removeBlocker]);
|
|
273
|
+
};
|
|
274
|
+
var useConfirmableBlocker = (blockerId, config) => {
|
|
275
|
+
const [isDialogOpen, setIsDialogOpen] = useState(false);
|
|
276
|
+
const [isExecuting, setIsExecuting] = useState(false);
|
|
277
|
+
useBlocker(
|
|
278
|
+
blockerId,
|
|
279
|
+
{
|
|
280
|
+
...config,
|
|
281
|
+
reason: config.reason ?? config.confirmMessage
|
|
282
|
+
},
|
|
283
|
+
isDialogOpen || isExecuting
|
|
284
|
+
);
|
|
285
|
+
const execute = useCallback(() => {
|
|
286
|
+
setIsDialogOpen(true);
|
|
287
|
+
}, []);
|
|
288
|
+
const onConfirm = useCallback(async () => {
|
|
289
|
+
setIsDialogOpen(false);
|
|
290
|
+
setIsExecuting(true);
|
|
291
|
+
try {
|
|
292
|
+
await config.onConfirm();
|
|
293
|
+
} catch (error) {
|
|
294
|
+
console.error("[UIBlocking] Confirmable action error:", error);
|
|
295
|
+
throw error;
|
|
296
|
+
} finally {
|
|
297
|
+
setIsExecuting(false);
|
|
298
|
+
}
|
|
299
|
+
}, [config]);
|
|
300
|
+
const onCancel = useCallback(() => {
|
|
301
|
+
setIsDialogOpen(false);
|
|
302
|
+
config.onCancel?.();
|
|
303
|
+
}, [config]);
|
|
304
|
+
return {
|
|
305
|
+
execute,
|
|
306
|
+
isDialogOpen,
|
|
307
|
+
isExecuting,
|
|
308
|
+
confirmConfig: {
|
|
309
|
+
title: config.confirmTitle ?? "Confirm Action",
|
|
310
|
+
message: config.confirmMessage,
|
|
311
|
+
confirmText: config.confirmButtonText ?? "Confirm",
|
|
312
|
+
cancelText: config.cancelButtonText ?? "Cancel"
|
|
313
|
+
},
|
|
314
|
+
onConfirm,
|
|
315
|
+
onCancel
|
|
316
|
+
};
|
|
317
|
+
};
|
|
318
|
+
var useScheduledBlocker = (blockerId, config) => {
|
|
319
|
+
const { addBlocker, removeBlocker } = useUIBlockingStore();
|
|
320
|
+
const timeoutsRef = useRef({});
|
|
321
|
+
const configRef = useConfigRef(config);
|
|
322
|
+
const cleanup = useCallback(() => {
|
|
323
|
+
if (timeoutsRef.current.start) {
|
|
324
|
+
clearTimeout(timeoutsRef.current.start);
|
|
325
|
+
timeoutsRef.current.start = void 0;
|
|
326
|
+
}
|
|
327
|
+
if (timeoutsRef.current.end) {
|
|
328
|
+
clearTimeout(timeoutsRef.current.end);
|
|
329
|
+
timeoutsRef.current.end = void 0;
|
|
330
|
+
}
|
|
331
|
+
}, []);
|
|
332
|
+
useEffect(() => {
|
|
333
|
+
const currentConfig = configRef.current;
|
|
334
|
+
const startTime = new Date(currentConfig.schedule.start).getTime();
|
|
335
|
+
const now = Date.now();
|
|
336
|
+
let endTime;
|
|
337
|
+
if (currentConfig.schedule.duration) {
|
|
338
|
+
endTime = startTime + currentConfig.schedule.duration;
|
|
339
|
+
} else if (currentConfig.schedule.end) {
|
|
340
|
+
endTime = new Date(currentConfig.schedule.end).getTime();
|
|
341
|
+
}
|
|
342
|
+
const scheduleEnd = (end) => {
|
|
343
|
+
const remainingTime = end - Date.now();
|
|
344
|
+
if (remainingTime > 0) {
|
|
345
|
+
timeoutsRef.current.end = setTimeout(() => {
|
|
346
|
+
removeBlocker(blockerId);
|
|
347
|
+
configRef.current.onScheduleEnd?.();
|
|
348
|
+
}, remainingTime);
|
|
349
|
+
}
|
|
350
|
+
};
|
|
351
|
+
if (startTime > now) {
|
|
352
|
+
timeoutsRef.current.start = setTimeout(() => {
|
|
353
|
+
const cfg = configRef.current;
|
|
354
|
+
addBlocker(blockerId, createBlockerConfig(cfg));
|
|
355
|
+
cfg.onScheduleStart?.();
|
|
356
|
+
if (endTime) {
|
|
357
|
+
scheduleEnd(endTime);
|
|
358
|
+
}
|
|
359
|
+
}, startTime - now);
|
|
360
|
+
} else if (endTime && endTime > now) {
|
|
361
|
+
addBlocker(blockerId, createBlockerConfig(currentConfig));
|
|
362
|
+
currentConfig.onScheduleStart?.();
|
|
363
|
+
scheduleEnd(endTime);
|
|
364
|
+
} else if (!endTime && startTime <= now) {
|
|
365
|
+
addBlocker(blockerId, createBlockerConfig(currentConfig));
|
|
366
|
+
currentConfig.onScheduleStart?.();
|
|
367
|
+
} else {
|
|
368
|
+
console.warn(`[UIBlocking] Schedule is in the past for blocker "${blockerId}"`);
|
|
369
|
+
}
|
|
370
|
+
return () => {
|
|
371
|
+
cleanup();
|
|
372
|
+
removeBlocker(blockerId);
|
|
373
|
+
};
|
|
374
|
+
}, [blockerId, addBlocker, removeBlocker, cleanup]);
|
|
375
|
+
};
|
|
376
|
+
|
|
377
|
+
// src/middleware/loggerMiddleware.utils.ts
|
|
378
|
+
var getActionEmoji = (action) => {
|
|
379
|
+
switch (action) {
|
|
380
|
+
case "add":
|
|
381
|
+
return "\u2795";
|
|
382
|
+
case "remove":
|
|
383
|
+
return "\u2796";
|
|
384
|
+
case "update":
|
|
385
|
+
return "\u{1F504}";
|
|
386
|
+
case "cancel":
|
|
387
|
+
return "\u274C";
|
|
388
|
+
case "timeout":
|
|
389
|
+
return "\u23F1\uFE0F";
|
|
390
|
+
default:
|
|
391
|
+
return "\u2753";
|
|
392
|
+
}
|
|
393
|
+
};
|
|
394
|
+
var extractConfigDetails = (config) => {
|
|
395
|
+
if (!config) {
|
|
396
|
+
return {};
|
|
397
|
+
}
|
|
398
|
+
const { scope, reason, priority } = config;
|
|
399
|
+
const details = {
|
|
400
|
+
scope,
|
|
401
|
+
reason
|
|
402
|
+
};
|
|
403
|
+
if (priority !== void 0) {
|
|
404
|
+
details.priority = priority;
|
|
405
|
+
}
|
|
406
|
+
return details;
|
|
407
|
+
};
|
|
408
|
+
var formatLogData = (context) => {
|
|
409
|
+
const configDetails = extractConfigDetails(context.config);
|
|
410
|
+
if (context.prevState) {
|
|
411
|
+
return {
|
|
412
|
+
config: configDetails,
|
|
413
|
+
prevState: extractConfigDetails(context.prevState)
|
|
414
|
+
};
|
|
415
|
+
}
|
|
416
|
+
return configDetails;
|
|
417
|
+
};
|
|
418
|
+
|
|
419
|
+
// src/middleware/loggerMiddleware.ts
|
|
420
|
+
var LOG_PREFIX = "[UIBlocking]";
|
|
421
|
+
var loggerMiddleware = (context) => {
|
|
422
|
+
const emoji = getActionEmoji(context.action);
|
|
423
|
+
const logData = formatLogData(context);
|
|
424
|
+
console.log(LOG_PREFIX, emoji, context.blockerId, logData);
|
|
425
|
+
};
|
|
426
|
+
|
|
427
|
+
// src/middleware/analyticsMiddleware.utils.ts
|
|
428
|
+
var capitalizeFirst = (str) => {
|
|
429
|
+
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
430
|
+
};
|
|
431
|
+
var buildEventData = (context) => {
|
|
432
|
+
const eventData = {
|
|
433
|
+
blocker_id: context.blockerId
|
|
434
|
+
};
|
|
435
|
+
const { scope, reason, priority } = context.config ?? {};
|
|
436
|
+
if (scope !== void 0) {
|
|
437
|
+
eventData.scope = Array.isArray(scope) ? scope.join(",") : scope;
|
|
438
|
+
}
|
|
439
|
+
if (reason !== void 0) {
|
|
440
|
+
eventData.reason = reason;
|
|
441
|
+
}
|
|
442
|
+
if (priority !== void 0) {
|
|
443
|
+
eventData.priority = priority;
|
|
444
|
+
}
|
|
445
|
+
return eventData;
|
|
446
|
+
};
|
|
447
|
+
var trackGoogleAnalytics = (action, eventData) => {
|
|
448
|
+
if (window.gtag) {
|
|
449
|
+
window.gtag("event", `ui_blocking_${action}`, eventData);
|
|
450
|
+
}
|
|
451
|
+
};
|
|
452
|
+
var trackMixpanel = (action, eventData) => {
|
|
453
|
+
if (window.mixpanel) {
|
|
454
|
+
const eventName = `UI Blocking ${action.split("_").map(capitalizeFirst).join(" ")}`;
|
|
455
|
+
window.mixpanel.track(eventName, eventData);
|
|
456
|
+
}
|
|
457
|
+
};
|
|
458
|
+
var trackAmplitude = (action, eventData) => {
|
|
459
|
+
if (window.amplitude) {
|
|
460
|
+
const eventName = `UI Blocking ${action.split("_").map(capitalizeFirst).join(" ")}`;
|
|
461
|
+
window.amplitude.track(eventName, eventData);
|
|
462
|
+
}
|
|
463
|
+
};
|
|
464
|
+
var trackCustom = (trackFn, action, eventData) => {
|
|
465
|
+
try {
|
|
466
|
+
trackFn(`ui_blocking_${action}`, eventData);
|
|
467
|
+
} catch {
|
|
468
|
+
}
|
|
469
|
+
};
|
|
470
|
+
|
|
471
|
+
// src/middleware/analyticsMiddleware.ts
|
|
472
|
+
var trackEvent = (config, context, eventData) => {
|
|
473
|
+
if ("provider" in config) {
|
|
474
|
+
switch (config.provider) {
|
|
475
|
+
case "ga":
|
|
476
|
+
trackGoogleAnalytics(context.action, eventData);
|
|
477
|
+
return;
|
|
478
|
+
case "mixpanel":
|
|
479
|
+
trackMixpanel(context.action, eventData);
|
|
480
|
+
return;
|
|
481
|
+
case "amplitude":
|
|
482
|
+
trackAmplitude(context.action, eventData);
|
|
483
|
+
return;
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
if ("track" in config) {
|
|
487
|
+
trackCustom(config.track, context.action, eventData);
|
|
488
|
+
}
|
|
489
|
+
};
|
|
490
|
+
var createAnalyticsMiddleware = (config) => {
|
|
491
|
+
return (context) => {
|
|
492
|
+
const eventData = buildEventData(context);
|
|
493
|
+
trackEvent(config, context, eventData);
|
|
494
|
+
};
|
|
495
|
+
};
|
|
496
|
+
|
|
497
|
+
// src/middleware/performanceMiddleware.utils.ts
|
|
498
|
+
var handleAddAction = (blockerId, timestamp, blockStartTimes) => {
|
|
499
|
+
blockStartTimes.set(blockerId, timestamp);
|
|
500
|
+
};
|
|
501
|
+
|
|
502
|
+
// src/middleware/performanceMiddleware.ts
|
|
503
|
+
var DEFAULT_SLOW_BLOCK_THRESHOLD = 3e3;
|
|
504
|
+
var ACTION_ADD = "add";
|
|
505
|
+
var ACTION_REMOVE = "remove";
|
|
506
|
+
var handleRemoveAction = (context, blockStartTimes, slowBlockThreshold, onSlowBlock) => {
|
|
507
|
+
const startTime = blockStartTimes.get(context.blockerId);
|
|
508
|
+
if (startTime === void 0) {
|
|
509
|
+
return;
|
|
510
|
+
}
|
|
511
|
+
const duration = context.timestamp - startTime;
|
|
512
|
+
blockStartTimes.delete(context.blockerId);
|
|
513
|
+
if (duration >= slowBlockThreshold) {
|
|
514
|
+
console.warn(`[UIBlocking] Slow block detected: "${context.blockerId}" took ${duration.toString()}ms`);
|
|
515
|
+
onSlowBlock?.(context.blockerId, duration);
|
|
516
|
+
}
|
|
517
|
+
};
|
|
518
|
+
var createPerformanceMiddleware = (config = {}) => {
|
|
519
|
+
const { slowBlockThreshold = DEFAULT_SLOW_BLOCK_THRESHOLD, onSlowBlock } = config;
|
|
520
|
+
const blockStartTimes = /* @__PURE__ */ new Map();
|
|
521
|
+
return (context) => {
|
|
522
|
+
if (context.action === ACTION_ADD) {
|
|
523
|
+
handleAddAction(context.blockerId, context.timestamp, blockStartTimes);
|
|
524
|
+
} else if (context.action === ACTION_REMOVE) {
|
|
525
|
+
handleRemoveAction(context, blockStartTimes, slowBlockThreshold, onSlowBlock);
|
|
526
|
+
}
|
|
527
|
+
};
|
|
528
|
+
};
|
|
174
529
|
|
|
175
|
-
export { useAsyncAction, useBlocker, useIsBlocked, useUIBlockingStore };
|
|
530
|
+
export { createAnalyticsMiddleware, createPerformanceMiddleware, loggerMiddleware, useAsyncAction, useBlocker, useConditionalBlocker, useConfirmableBlocker, useIsBlocked, useScheduledBlocker, useUIBlockingStore };
|
|
176
531
|
//# sourceMappingURL=index.js.map
|
|
177
532
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/store/uiBlockingStore.constants.ts","../src/store/uiBlockingStore.utils.ts","../src/store/uiBlockingStore.actions.ts","../src/store/uiBlockingStore.config.ts","../src/store/uiBlockingStore.store.ts","../src/hooks/useBlocker.ts","../src/hooks/useIsBlocked.ts","../src/hooks/useAsyncAction.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Default values and constants for UI Blocking Store\n */\n\n/**\n * Default scope for blockers\n */\nexport const DEFAULT_SCOPE = \"global\" as const;\n\n/**\n * Default reason when none is provided\n */\nexport const DEFAULT_REASON = \"Unknown\" as const;\n\n/**\n * Default priority for blockers\n */\nexport const DEFAULT_PRIORITY = 0 as const;\n\n/**\n * Default priority for async actions\n */\nexport const ASYNC_ACTION_PRIORITY = 20 as const;\n\n/**\n * DevTools store name\n */\nexport const DEVTOOLS_NAME = \"UIBlocking\" as const;\n","/**\n * Normalize scope to array format\n *\n * @param scope - Single scope or array of scopes\n * @returns Array of scopes\n *\n * @internal\n */\nexport function normalizeScopeToArray(scope: string | ReadonlyArray<string>): Array<string> {\n if (typeof scope === \"string\") {\n return [scope];\n }\n return Array.from(scope);\n}\n","import type { StateCreator } from \"zustand\";\nimport type {\n BlockerConfig,\n BlockerInfo,\n StoredBlocker,\n UIBlockingStore,\n} from \"./uiBlockingStore.types\";\nimport { DEFAULT_SCOPE, DEFAULT_REASON, DEFAULT_PRIORITY } from \"./uiBlockingStore.constants\";\nimport { normalizeScopeToArray } from \"./uiBlockingStore.utils\";\n\n/**\n * UI Blocking Store Slice\n *\n * Implements the state and actions for UI blocking management.\n * This slice follows the Zustand slice pattern for better code organization.\n */\nexport const createUIBlockingActions: StateCreator<UIBlockingStore, [], [], UIBlockingStore> = (\n set,\n get\n) => ({\n // State\n activeBlockers: new Map(),\n\n // Actions\n /**\n * Add a blocker to the store\n *\n * @param id - Unique blocker identifier\n * @param config - Optional blocker configuration\n *\n */\n addBlocker: (id: string, config: BlockerConfig = {}): void => {\n set((state) => {\n const newBlockers = new Map(state.activeBlockers);\n const storedBlocker: StoredBlocker = {\n scope: config.scope ?? DEFAULT_SCOPE,\n reason: config.reason ?? DEFAULT_REASON,\n priority: config.priority ?? DEFAULT_PRIORITY,\n timestamp: config.timestamp ?? Date.now(),\n };\n newBlockers.set(id, storedBlocker);\n\n return { activeBlockers: newBlockers };\n });\n },\n\n /**\n * Remove a blocker from the store\n *\n * @param id - Blocker identifier to remove\n *\n */\n removeBlocker: (id: string): void => {\n set((state) => {\n const newBlockers = new Map(state.activeBlockers);\n newBlockers.delete(id);\n\n return { activeBlockers: newBlockers };\n });\n },\n\n /**\n * Check if a scope is currently blocked\n *\n * @param scope - Scope or array of scopes to check (default: \"global\")\n * @returns True if any of the scopes are blocked\n *\n */\n isBlocked: (scope: string | ReadonlyArray<string> = DEFAULT_SCOPE): boolean => {\n const { activeBlockers } = get();\n const scopes = normalizeScopeToArray(scope);\n\n for (const [, blocker] of activeBlockers) {\n // Global blocking blocks everything\n if (blocker.scope === DEFAULT_SCOPE) {\n return true;\n }\n\n // Check scope intersection\n const blockerScopes = normalizeScopeToArray(blocker.scope);\n\n if (scopes.some((s) => blockerScopes.includes(s))) {\n return true;\n }\n }\n\n return false;\n },\n\n /**\n * Get detailed blocking information for a specific scope\n *\n * @param scope - Scope to get blocking information for\n * @returns Array of blockers affecting the scope, sorted by priority (highest first)\n *\n */\n getBlockingInfo: (scope: string): ReadonlyArray<BlockerInfo> => {\n const { activeBlockers } = get();\n const blockers: Array<BlockerInfo> = [];\n\n for (const [id, blocker] of activeBlockers) {\n const blockerScopes = normalizeScopeToArray(blocker.scope);\n\n if (blocker.scope === DEFAULT_SCOPE || blockerScopes.includes(scope)) {\n blockers.push({ id, ...blocker });\n }\n }\n\n return blockers.sort((a, b) => b.priority - a.priority);\n },\n\n /**\n * Clear all blockers from the store\n */\n clearAllBlockers: (): void => {\n set({ activeBlockers: new Map() });\n },\n\n /**\n * Clear all blockers for a specific scope\n *\n * @param scope - Scope to clear blockers for\n *\n */\n clearBlockersForScope: (scope: string): void => {\n set((state) => {\n const newBlockers = new Map();\n\n for (const [id, blocker] of state.activeBlockers) {\n const blockerScopes = normalizeScopeToArray(blocker.scope);\n\n if (!blockerScopes.includes(scope)) {\n newBlockers.set(id, blocker);\n }\n }\n\n return { activeBlockers: newBlockers };\n });\n },\n});\n","import type { DevtoolsOptions } from \"zustand/middleware\";\nimport { DEVTOOLS_NAME } from \"./uiBlockingStore.constants\";\n\n/**\n * DevTools configuration for UI Blocking Store\n *\n * Enables Redux DevTools integration only in development mode\n * for better debugging experience without production overhead.\n */\nexport const devtoolsConfig: DevtoolsOptions = {\n name: DEVTOOLS_NAME,\n enabled: process.env.NODE_ENV !== \"production\",\n};\n","import { create } from \"zustand\";\nimport { devtools } from \"zustand/middleware\";\nimport { createUIBlockingActions } from \"./uiBlockingStore.actions\";\nimport { devtoolsConfig } from \"./uiBlockingStore.config\";\nimport type { UIBlockingStore } from \"./uiBlockingStore.types\";\n\n/**\n * UI Blocking Store\n *\n * Global Zustand store for managing UI blocking state across the application.\n *\n * Features:\n * - Priority-based blocking system\n * - Scope-based blocking (global, specific areas, or multiple scopes)\n * - Automatic cleanup on unmount (when using hooks)\n * - DevTools integration for debugging\n *\n */\nexport const useUIBlockingStore = create<UIBlockingStore>()(\n devtools(createUIBlockingActions, devtoolsConfig)\n);\n","import { useEffect } from \"react\";\nimport { useUIBlockingStore } from \"../store\";\nimport type { BlockerConfig } from \"../store\";\n\n/**\n * Hook to automatically add and remove a blocker based on component lifecycle\n *\n * Automatically adds a blocker when the component mounts (or when activated)\n * and removes it when the component unmounts (or when deactivated).\n *\n * @param blockerId - Unique identifier for the blocker\n * @param config - Blocker configuration (scope, reason, priority)\n * @param isActive - Whether the blocker is currently active (default: true)\n *\n */\nexport const useBlocker = (blockerId: string, config: BlockerConfig, isActive = true): void => {\n const { addBlocker, removeBlocker } = useUIBlockingStore();\n\n useEffect(() => {\n if (!isActive || !blockerId) {\n return;\n }\n\n addBlocker(blockerId, config);\n\n return () => {\n removeBlocker(blockerId);\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [blockerId, isActive, addBlocker, removeBlocker]);\n};\n","import { useUIBlockingStore } from \"../store\";\n\n/**\n * Hook to check if a scope is currently blocked\n *\n * Returns a boolean indicating whether the specified scope(s) are blocked.\n * Automatically re-renders when the blocking state changes.\n *\n * @param scope - Scope or array of scopes to check (default: \"global\")\n * @returns True if any of the specified scopes are blocked\n *\n */\nexport const useIsBlocked = (scope?: string | ReadonlyArray<string>): boolean => {\n return useUIBlockingStore((state) => state.isBlocked(scope));\n};\n","import { useCallback } from \"react\";\nimport { useUIBlockingStore } from \"../store\";\nimport { ASYNC_ACTION_PRIORITY } from \"../store\";\n\n/**\n * Hook to wrap async functions with automatic blocking/unblocking\n *\n * Creates a wrapper function that automatically adds a blocker before executing\n * the async function and removes it after completion (success or failure).\n *\n * @template T - The return type of the async function\n * @param actionId - Unique identifier for the action (used in blocker ID and reason)\n * @param scope - Scope(s) to block during execution (default: \"global\")\n * @returns Function that wraps async functions with blocking behavior\n *\n */\nexport const useAsyncAction = <T = unknown>(\n actionId: string,\n scope?: string | ReadonlyArray<string>\n): ((asyncFn: () => Promise<T>) => Promise<T>) => {\n const { addBlocker, removeBlocker } = useUIBlockingStore();\n\n const executeWithBlocking = useCallback(\n async (asyncFn: () => Promise<T>): Promise<T> => {\n const blockerId = `${actionId}-${Date.now().toString()}`;\n\n try {\n addBlocker(blockerId, {\n scope,\n reason: `Executing ${actionId}`,\n priority: ASYNC_ACTION_PRIORITY,\n });\n\n return await asyncFn();\n } finally {\n removeBlocker(blockerId);\n }\n },\n [actionId, scope, addBlocker, removeBlocker]\n );\n\n return executeWithBlocking;\n};\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/store/uiBlockingStore.constants.ts","../src/store/uiBlockingStore.utils.ts","../src/store/uiBlockingStore.actions.ts","../src/store/uiBlockingStore.config.ts","../src/store/uiBlockingStore.store.ts","../src/hooks/useBlocker.ts","../src/hooks/useIsBlocked.ts","../src/hooks/useAsyncAction.ts","../src/hooks/useConfigRef.ts","../src/hooks/useBlocker.utils.ts","../src/hooks/useConditionalBlocker.ts","../src/hooks/useConfirmableBlocker.ts","../src/hooks/useScheduledBlocker.ts","../src/middleware/loggerMiddleware.utils.ts","../src/middleware/loggerMiddleware.ts","../src/middleware/analyticsMiddleware.utils.ts","../src/middleware/analyticsMiddleware.ts","../src/middleware/performanceMiddleware.utils.ts","../src/middleware/performanceMiddleware.ts"],"names":["useEffect","useRef","useCallback"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Default values and constants for UI Blocking Store\n */\n\n/**\n * Default scope for blockers\n */\nexport const DEFAULT_SCOPE = \"global\" as const;\n\n/**\n * Default reason when none is provided\n */\nexport const DEFAULT_REASON = \"Unknown\" as const;\n\n/**\n * Default priority for blockers\n */\nexport const DEFAULT_PRIORITY = 0 as const;\n\n/**\n * Default priority for async actions\n */\nexport const ASYNC_ACTION_PRIORITY = 20 as const;\n\n/**\n * DevTools store name\n */\nexport const DEVTOOLS_NAME = \"UIBlocking\" as const;\n","/**\n * Normalize scope to array format\n *\n * @param scope - Single scope or array of scopes\n * @returns Array of scopes\n *\n * @internal\n */\nexport function normalizeScopeToArray(scope: string | ReadonlyArray<string>): Array<string> {\n if (typeof scope === \"string\") {\n return [scope];\n }\n return Array.from(scope);\n}\n","import type { StateCreator } from \"zustand\";\nimport type { BlockerConfig, BlockerInfo, StoredBlocker, UIBlockingStore } from \"./uiBlockingStore.types\";\nimport { DEFAULT_SCOPE, DEFAULT_REASON, DEFAULT_PRIORITY } from \"./uiBlockingStore.constants\";\nimport { normalizeScopeToArray } from \"./uiBlockingStore.utils\";\nimport { Middleware, MiddlewareContext } from \"../middleware\";\n\n/**\n * UI Blocking Store Slice\n *\n * Implements the state and actions for UI blocking management.\n * This slice follows the Zustand slice pattern for better code organization.\n */\nexport const createUIBlockingActions: StateCreator<UIBlockingStore, [], [], UIBlockingStore> = (set, get) => ({\n // State\n activeBlockers: new Map(),\n middlewares: new Map(),\n\n registerMiddleware: (name: string, middleware: Middleware) => {\n set((state) => {\n const newMiddlewares = new Map(state.middlewares);\n newMiddlewares.set(name, middleware);\n return { middlewares: newMiddlewares };\n });\n console.log(`[UIBlocking] Registered middleware: ${name}`);\n },\n\n unregisterMiddleware: (name: string) => {\n set((state) => {\n const newMiddlewares = new Map(state.middlewares);\n newMiddlewares.delete(name);\n return { middlewares: newMiddlewares };\n });\n console.log(`[UIBlocking] Unregistered middleware: ${name}`);\n },\n\n runMiddlewares: async (context: MiddlewareContext) => {\n const { middlewares } = get();\n\n for (const [name, middleware] of middlewares) {\n try {\n await middleware(context);\n } catch (error) {\n console.error(`[UIBlocking] Middleware \"${name}\" error:`, error);\n }\n }\n },\n\n // Actions\n /**\n * Add a blocker to the store\n *\n * @param id - Unique blocker identifier\n * @param config - Optional blocker configuration\n *\n */\n addBlocker: (id: string, config: BlockerConfig = {}): void => {\n set((state) => {\n const newBlockers = new Map(state.activeBlockers);\n const storedBlocker: StoredBlocker = {\n scope: config.scope ?? DEFAULT_SCOPE,\n reason: config.reason ?? DEFAULT_REASON,\n priority: config.priority ?? DEFAULT_PRIORITY,\n timestamp: config.timestamp ?? Date.now(),\n };\n newBlockers.set(id, storedBlocker);\n\n return { activeBlockers: newBlockers };\n });\n\n void get().runMiddlewares({\n action: \"add\",\n blockerId: id,\n config: {\n scope: config.scope,\n reason: config.reason,\n priority: config.priority,\n },\n timestamp: Date.now(),\n });\n },\n\n /**\n * Remove a blocker from the store\n *\n * @param id - Blocker identifier to remove\n *\n */\n removeBlocker: (id: string): void => {\n const prevBlocker = get().activeBlockers.get(id);\n\n set((state) => {\n const newBlockers = new Map(state.activeBlockers);\n newBlockers.delete(id);\n\n return { activeBlockers: newBlockers };\n });\n\n if (prevBlocker) {\n void get().runMiddlewares({\n action: \"remove\",\n blockerId: id,\n config: prevBlocker,\n timestamp: Date.now(),\n prevState: prevBlocker,\n });\n }\n },\n\n /**\n * Check if a scope is currently blocked\n *\n * @param scope - Scope or array of scopes to check (default: \"global\")\n * @returns True if any of the scopes are blocked\n *\n */\n isBlocked: (scope: string | ReadonlyArray<string> = DEFAULT_SCOPE): boolean => {\n const { activeBlockers } = get();\n const scopes = normalizeScopeToArray(scope);\n\n for (const [, blocker] of activeBlockers) {\n // Global blocking blocks everything\n if (blocker.scope === DEFAULT_SCOPE) {\n return true;\n }\n\n // Check scope intersection\n const blockerScopes = normalizeScopeToArray(blocker.scope);\n\n if (scopes.some((s) => blockerScopes.includes(s))) {\n return true;\n }\n }\n\n return false;\n },\n\n /**\n * Get detailed blocking information for a specific scope\n *\n * @param scope - Scope to get blocking information for\n * @returns Array of blockers affecting the scope, sorted by priority (highest first)\n *\n */\n getBlockingInfo: (scope: string): ReadonlyArray<BlockerInfo> => {\n const { activeBlockers } = get();\n const blockers: Array<BlockerInfo> = [];\n\n for (const [id, blocker] of activeBlockers) {\n const blockerScopes = normalizeScopeToArray(blocker.scope);\n\n if (blocker.scope === DEFAULT_SCOPE || blockerScopes.includes(scope)) {\n blockers.push({ id, ...blocker });\n }\n }\n\n return blockers.sort((a, b) => b.priority - a.priority);\n },\n\n /**\n * Clear all blockers from the store\n */\n clearAllBlockers: (): void => {\n set({ activeBlockers: new Map() });\n },\n\n /**\n * Clear all blockers for a specific scope\n *\n * @param scope - Scope to clear blockers for\n *\n */\n clearBlockersForScope: (scope: string): void => {\n set((state) => {\n const newBlockers = new Map();\n\n for (const [id, blocker] of state.activeBlockers) {\n const blockerScopes = normalizeScopeToArray(blocker.scope);\n\n if (!blockerScopes.includes(scope)) {\n newBlockers.set(id, blocker);\n }\n }\n\n return { activeBlockers: newBlockers };\n });\n },\n});\n","import type { DevtoolsOptions } from \"zustand/middleware\";\nimport { DEVTOOLS_NAME } from \"./uiBlockingStore.constants\";\n\n/**\n * DevTools configuration for UI Blocking Store\n *\n * Enables Redux DevTools integration only in development mode\n * for better debugging experience without production overhead.\n */\nexport const devtoolsConfig: DevtoolsOptions = {\n name: DEVTOOLS_NAME,\n enabled: process.env.NODE_ENV !== \"production\",\n};\n","import { create } from \"zustand\";\nimport { devtools } from \"zustand/middleware\";\nimport { createUIBlockingActions } from \"./uiBlockingStore.actions\";\nimport { devtoolsConfig } from \"./uiBlockingStore.config\";\nimport type { UIBlockingStore } from \"./uiBlockingStore.types\";\n\n/**\n * UI Blocking Store\n *\n * Global Zustand store for managing UI blocking state across the application.\n *\n * Features:\n * - Priority-based blocking system\n * - Scope-based blocking (global, specific areas, or multiple scopes)\n * - Automatic cleanup on unmount (when using hooks)\n * - DevTools integration for debugging\n *\n */\nexport const useUIBlockingStore = create<UIBlockingStore>()(\n devtools(createUIBlockingActions, devtoolsConfig)\n);\n","import { useEffect } from \"react\";\nimport { useUIBlockingStore } from \"../store\";\nimport type { BlockerConfig } from \"../store\";\n\n/**\n * Hook to automatically add and remove a blocker based on component lifecycle\n *\n * Automatically adds a blocker when the component mounts (or when activated)\n * and removes it when the component unmounts (or when deactivated).\n *\n * @param blockerId - Unique identifier for the blocker\n * @param config - Blocker configuration (scope, reason, priority)\n * @param isActive - Whether the blocker is currently active (default: true)\n *\n */\nexport const useBlocker = (blockerId: string, config: BlockerConfig, isActive = true): void => {\n const { addBlocker, removeBlocker } = useUIBlockingStore();\n\n useEffect(() => {\n if (!isActive || !blockerId) {\n return;\n }\n\n addBlocker(blockerId, config);\n\n return () => {\n removeBlocker(blockerId);\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [blockerId, isActive, addBlocker, removeBlocker]);\n};\n","import { useUIBlockingStore } from \"../store\";\n\n/**\n * Hook to check if a scope is currently blocked\n *\n * Returns a boolean indicating whether the specified scope(s) are blocked.\n * Automatically re-renders when the blocking state changes.\n *\n * @param scope - Scope or array of scopes to check (default: \"global\")\n * @returns True if any of the specified scopes are blocked\n *\n */\nexport const useIsBlocked = (scope?: string | ReadonlyArray<string>): boolean => {\n return useUIBlockingStore((state) => state.isBlocked(scope));\n};\n","import { useCallback } from \"react\";\nimport { useUIBlockingStore } from \"../store\";\nimport { ASYNC_ACTION_PRIORITY } from \"../store\";\n\n/**\n * Hook to wrap async functions with automatic blocking/unblocking\n *\n * Creates a wrapper function that automatically adds a blocker before executing\n * the async function and removes it after completion (success or failure).\n *\n * @template T - The return type of the async function\n * @param actionId - Unique identifier for the action (used in blocker ID and reason)\n * @param scope - Scope(s) to block during execution (default: \"global\")\n * @returns Function that wraps async functions with blocking behavior\n *\n */\nexport const useAsyncAction = <T = unknown>(\n actionId: string,\n scope?: string | ReadonlyArray<string>\n): ((asyncFn: () => Promise<T>) => Promise<T>) => {\n const { addBlocker, removeBlocker } = useUIBlockingStore();\n\n const executeWithBlocking = useCallback(\n async (asyncFn: () => Promise<T>): Promise<T> => {\n const blockerId = `${actionId}-${Date.now().toString()}`;\n\n try {\n addBlocker(blockerId, {\n scope,\n reason: `Executing ${actionId}`,\n priority: ASYNC_ACTION_PRIORITY,\n });\n\n return await asyncFn();\n } finally {\n removeBlocker(blockerId);\n }\n },\n [actionId, scope, addBlocker, removeBlocker]\n );\n\n return executeWithBlocking;\n};\n","import { useRef, useEffect, RefObject } from \"react\";\n\n/**\n * Keeps a ref updated with the latest config value.\n * Useful for accessing current config in callbacks without stale closures.\n *\n * @param config - Configuration object to track\n * @returns Mutable ref that always contains the latest config\n *\n */\nexport function useConfigRef<T>(config: T): RefObject<T> {\n const configRef = useRef(config);\n\n useEffect(() => {\n configRef.current = config;\n }, [config]);\n\n return configRef;\n}\n","import { BlockerConfig } from \"../store\";\n\n/**\n * Creates a blocker configuration object from partial config.\n * Filters out undefined values to create a clean BlockerConfig.\n *\n * @param config - Partial blocker configuration\n * @returns Complete BlockerConfig object\n *\n */\nexport function createBlockerConfig(config: {\n scope?: string | ReadonlyArray<string>;\n reason?: string;\n priority?: number;\n}): BlockerConfig {\n return {\n scope: config.scope,\n reason: config.reason,\n priority: config.priority,\n };\n}\n","import { useRef, useEffect, useCallback } from \"react\";\nimport { useUIBlockingStore } from \"../store\";\nimport { ConditionalBlockerConfig } from \"./useConditionalBlocker.types\";\nimport { useConfigRef } from \"./useConfigRef\";\nimport { createBlockerConfig } from \"./useBlocker.utils\";\n\n/**\n * Hook for conditional blocking based on a condition function.\n *\n * Periodically checks a condition and blocks/unblocks based on the result.\n * The condition is evaluated at the specified interval (default 1000ms).\n *\n * @param blockerId - Unique identifier for this blocker\n * @param config - Configuration including condition function and check interval\n *\n */\nexport const useConditionalBlocker = <TState = unknown>(\n blockerId: string,\n config: ConditionalBlockerConfig<TState>\n): void => {\n const { addBlocker, removeBlocker } = useUIBlockingStore();\n const intervalRef = useRef<ReturnType<typeof setInterval> | null>(null);\n const isBlockedRef = useRef(false);\n const configRef = useConfigRef(config);\n\n const checkCondition = useCallback(() => {\n const currentConfig = configRef.current;\n const shouldBlock = currentConfig.condition(currentConfig.state);\n\n // No state change needed\n if (shouldBlock === isBlockedRef.current) {\n return;\n }\n\n if (shouldBlock) {\n addBlocker(blockerId, createBlockerConfig(currentConfig));\n isBlockedRef.current = true;\n } else {\n removeBlocker(blockerId);\n isBlockedRef.current = false;\n }\n }, [blockerId, addBlocker, removeBlocker, configRef]);\n\n useEffect(() => {\n checkCondition();\n\n const interval = configRef.current.checkInterval ?? 1000;\n intervalRef.current = setInterval(checkCondition, interval);\n\n return (): void => {\n if (intervalRef.current) {\n clearInterval(intervalRef.current);\n intervalRef.current = null;\n }\n if (isBlockedRef.current) {\n removeBlocker(blockerId);\n isBlockedRef.current = false;\n }\n };\n // configRef is stable and doesn't need to be in dependencies\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [checkCondition, blockerId, removeBlocker]);\n};\n","import { useState, useCallback } from \"react\";\nimport { useBlocker } from \"./useBlocker\";\nimport { ConfirmableBlockerConfig, UseConfirmableBlockerReturn } from \"./useConfirmableBlocker.types\";\n\n/**\n * Hook for creating a confirmable action with blocking UI\n *\n */\nexport const useConfirmableBlocker = (\n blockerId: string,\n config: ConfirmableBlockerConfig\n): UseConfirmableBlockerReturn => {\n const [isDialogOpen, setIsDialogOpen] = useState(false);\n const [isExecuting, setIsExecuting] = useState(false);\n\n useBlocker(\n blockerId,\n {\n ...config,\n reason: config.reason ?? config.confirmMessage,\n },\n isDialogOpen || isExecuting\n );\n\n const execute = useCallback(() => {\n setIsDialogOpen(true);\n }, []);\n\n const onConfirm = useCallback(async () => {\n setIsDialogOpen(false);\n setIsExecuting(true);\n\n try {\n await config.onConfirm();\n } catch (error) {\n console.error(\"[UIBlocking] Confirmable action error:\", error);\n throw error;\n } finally {\n setIsExecuting(false);\n }\n }, [config]);\n\n const onCancel = useCallback(() => {\n setIsDialogOpen(false);\n config.onCancel?.();\n }, [config]);\n\n return {\n execute,\n isDialogOpen,\n isExecuting,\n confirmConfig: {\n title: config.confirmTitle ?? \"Confirm Action\",\n message: config.confirmMessage,\n confirmText: config.confirmButtonText ?? \"Confirm\",\n cancelText: config.cancelButtonText ?? \"Cancel\",\n },\n onConfirm,\n onCancel,\n };\n};\n","import { useEffect, useRef, useCallback } from \"react\";\nimport { useUIBlockingStore } from \"../store\";\nimport { ScheduledBlockerConfig } from \"./useScheduledBlocker.types\";\nimport { useConfigRef } from \"./useConfigRef\";\nimport { createBlockerConfig } from \"./useBlocker.utils\";\n\n/**\n * Hook for scheduling UI blocking for a specific time period.\n *\n * Blocks UI during a scheduled maintenance window or time period.\n * Supports start/end times as Date objects, ISO strings, or timestamps.\n * If both duration and end are provided, duration takes precedence.\n *\n * @param blockerId - Unique identifier for this blocker\n * @param config - Configuration including schedule and callbacks\n *\n */\nexport const useScheduledBlocker = (blockerId: string, config: ScheduledBlockerConfig): void => {\n const { addBlocker, removeBlocker } = useUIBlockingStore();\n const timeoutsRef = useRef<{\n start?: ReturnType<typeof setTimeout>;\n end?: ReturnType<typeof setTimeout>;\n }>({});\n const configRef = useConfigRef(config);\n\n const cleanup = useCallback(() => {\n if (timeoutsRef.current.start) {\n clearTimeout(timeoutsRef.current.start);\n timeoutsRef.current.start = undefined;\n }\n if (timeoutsRef.current.end) {\n clearTimeout(timeoutsRef.current.end);\n timeoutsRef.current.end = undefined;\n }\n }, []);\n\n useEffect(() => {\n const currentConfig = configRef.current;\n const startTime = new Date(currentConfig.schedule.start).getTime();\n const now = Date.now();\n\n let endTime: number | undefined;\n if (currentConfig.schedule.duration) {\n endTime = startTime + currentConfig.schedule.duration;\n } else if (currentConfig.schedule.end) {\n endTime = new Date(currentConfig.schedule.end).getTime();\n }\n\n const scheduleEnd = (end: number): void => {\n const remainingTime = end - Date.now();\n if (remainingTime > 0) {\n timeoutsRef.current.end = setTimeout(() => {\n removeBlocker(blockerId);\n configRef.current.onScheduleEnd?.();\n }, remainingTime);\n }\n };\n\n // If start time hasn't arrived yet\n if (startTime > now) {\n timeoutsRef.current.start = setTimeout(() => {\n const cfg = configRef.current;\n addBlocker(blockerId, createBlockerConfig(cfg));\n cfg.onScheduleStart?.();\n\n if (endTime) {\n scheduleEnd(endTime);\n }\n }, startTime - now);\n }\n // If already in blocking period\n else if (endTime && endTime > now) {\n addBlocker(blockerId, createBlockerConfig(currentConfig));\n currentConfig.onScheduleStart?.();\n scheduleEnd(endTime);\n }\n // If schedule without end/duration\n else if (!endTime && startTime <= now) {\n addBlocker(blockerId, createBlockerConfig(currentConfig));\n currentConfig.onScheduleStart?.();\n } else {\n console.warn(`[UIBlocking] Schedule is in the past for blocker \"${blockerId}\"`);\n }\n\n return (): void => {\n cleanup();\n removeBlocker(blockerId);\n };\n // configRef is stable and doesn't need to be in dependencies\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [blockerId, addBlocker, removeBlocker, cleanup]);\n};\n","import { MiddlewareBlockerConfig, MiddlewareContext } from \"./middleware.types\";\n\nexport const getActionEmoji = (action: string): string => {\n switch (action) {\n case \"add\":\n return \"➕\";\n case \"remove\":\n return \"➖\";\n case \"update\":\n return \"🔄\";\n case \"cancel\":\n return \"❌\";\n case \"timeout\":\n return \"⏱️\";\n default:\n return \"❓\";\n }\n};\n\nconst extractConfigDetails = (config?: MiddlewareBlockerConfig): Record<string, unknown> => {\n if (!config) {\n return {};\n }\n\n const { scope, reason, priority } = config;\n\n const details: Record<string, unknown> = {\n scope,\n reason,\n };\n\n if (priority !== undefined) {\n details.priority = priority;\n }\n\n return details;\n};\n\nexport const formatLogData = (context: MiddlewareContext): Record<string, unknown> => {\n const configDetails = extractConfigDetails(context.config);\n\n if (context.prevState) {\n return {\n config: configDetails,\n prevState: extractConfigDetails(context.prevState),\n };\n }\n\n return configDetails;\n};\n","import { getActionEmoji, formatLogData } from \"./loggerMiddleware.utils\";\nimport { Middleware } from \"./middleware.types\";\n\nconst LOG_PREFIX = \"[UIBlocking]\";\n\nexport const loggerMiddleware: Middleware = (context) => {\n const emoji = getActionEmoji(context.action);\n const logData = formatLogData(context);\n\n console.log(LOG_PREFIX, emoji, context.blockerId, logData);\n};\n","import { AnalyticsEventData } from \"./analyticsMiddleware.types\";\nimport { MiddlewareContext } from \"./middleware.types\";\n\nconst capitalizeFirst = (str: string): string => {\n return str.charAt(0).toUpperCase() + str.slice(1);\n};\n\nexport const buildEventData = (context: MiddlewareContext): AnalyticsEventData => {\n const eventData: AnalyticsEventData = {\n blocker_id: context.blockerId,\n };\n const { scope, reason, priority } = context.config ?? {};\n\n if (scope !== undefined) {\n eventData.scope = Array.isArray(scope) ? scope.join(\",\") : scope;\n }\n\n if (reason !== undefined) {\n eventData.reason = reason;\n }\n\n if (priority !== undefined) {\n eventData.priority = priority;\n }\n\n return eventData;\n};\n\nexport const trackGoogleAnalytics = (action: string, eventData: AnalyticsEventData): void => {\n if (window.gtag) {\n window.gtag(\"event\", `ui_blocking_${action}`, eventData);\n }\n};\n\nexport const trackMixpanel = (action: string, eventData: AnalyticsEventData): void => {\n if (window.mixpanel) {\n const eventName = `UI Blocking ${action.split(\"_\").map(capitalizeFirst).join(\" \")}`;\n\n window.mixpanel.track(eventName, eventData);\n }\n};\n\nexport const trackAmplitude = (action: string, eventData: AnalyticsEventData): void => {\n if (window.amplitude) {\n const eventName = `UI Blocking ${action.split(\"_\").map(capitalizeFirst).join(\" \")}`;\n\n window.amplitude.track(eventName, eventData);\n }\n};\n\nexport const trackCustom = (\n trackFn: (event: string, data: AnalyticsEventData) => void,\n action: string,\n eventData: AnalyticsEventData\n): void => {\n try {\n trackFn(`ui_blocking_${action}`, eventData);\n } catch {\n // Silently ignore errors in custom tracker\n }\n};\n","import { AnalyticsConfig, AnalyticsEventData } from \"./analyticsMiddleware.types\";\nimport {\n buildEventData,\n trackGoogleAnalytics,\n trackMixpanel,\n trackAmplitude,\n trackCustom,\n} from \"./analyticsMiddleware.utils\";\nimport { Middleware, MiddlewareContext } from \"./middleware.types\";\n\nconst trackEvent = (config: AnalyticsConfig, context: MiddlewareContext, eventData: AnalyticsEventData): void => {\n if (\"provider\" in config) {\n switch (config.provider) {\n case \"ga\":\n trackGoogleAnalytics(context.action, eventData);\n return;\n case \"mixpanel\":\n trackMixpanel(context.action, eventData);\n return;\n case \"amplitude\":\n trackAmplitude(context.action, eventData);\n return;\n }\n }\n\n if (\"track\" in config) {\n trackCustom(config.track, context.action, eventData);\n }\n};\n\nexport const createAnalyticsMiddleware = (config: AnalyticsConfig): Middleware => {\n return (context) => {\n const eventData = buildEventData(context);\n\n trackEvent(config, context, eventData);\n };\n};\n","export const handleAddAction = (blockerId: string, timestamp: number, blockStartTimes: Map<string, number>): void => {\n blockStartTimes.set(blockerId, timestamp);\n};\n","import { Middleware, MiddlewareContext } from \"./middleware.types\";\nimport { PerformanceConfig } from \"./performanceMiddleware.types\";\nimport { handleAddAction } from \"./performanceMiddleware.utils\";\n\nconst DEFAULT_SLOW_BLOCK_THRESHOLD = 3000;\nconst ACTION_ADD = \"add\";\nconst ACTION_REMOVE = \"remove\";\n\nconst handleRemoveAction = (\n context: MiddlewareContext,\n blockStartTimes: Map<string, number>,\n slowBlockThreshold: number,\n onSlowBlock?: (blockerId: string, duration: number) => void\n): void => {\n const startTime = blockStartTimes.get(context.blockerId);\n\n if (startTime === undefined) {\n return;\n }\n\n const duration = context.timestamp - startTime;\n blockStartTimes.delete(context.blockerId);\n\n if (duration >= slowBlockThreshold) {\n console.warn(`[UIBlocking] Slow block detected: \"${context.blockerId}\" took ${duration.toString()}ms`);\n\n onSlowBlock?.(context.blockerId, duration);\n }\n};\n\nexport const createPerformanceMiddleware = (config: PerformanceConfig = {}): Middleware => {\n const { slowBlockThreshold = DEFAULT_SLOW_BLOCK_THRESHOLD, onSlowBlock } = config;\n const blockStartTimes = new Map<string, number>();\n\n return (context) => {\n if (context.action === ACTION_ADD) {\n handleAddAction(context.blockerId, context.timestamp, blockStartTimes);\n } else if (context.action === ACTION_REMOVE) {\n handleRemoveAction(context, blockStartTimes, slowBlockThreshold, onSlowBlock);\n }\n };\n};\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@okyrychenko-dev/react-action-guard",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Elegant UI blocking management for React applications with priorities, scopes, and automatic cleanup",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -9,10 +9,11 @@
|
|
|
9
9
|
"exports": {
|
|
10
10
|
".": {
|
|
11
11
|
"types": "./dist/index.d.ts",
|
|
12
|
-
"import": "./dist/index.
|
|
13
|
-
"require": "./dist/index.
|
|
12
|
+
"import": "./dist/index.js",
|
|
13
|
+
"require": "./dist/index.cjs"
|
|
14
14
|
}
|
|
15
15
|
},
|
|
16
|
+
"sideEffects": false,
|
|
16
17
|
"files": [
|
|
17
18
|
"dist",
|
|
18
19
|
"README.md",
|
|
@@ -43,7 +44,7 @@
|
|
|
43
44
|
"loading",
|
|
44
45
|
"async-operations"
|
|
45
46
|
],
|
|
46
|
-
"author": "
|
|
47
|
+
"author": "Olexii Kyrychenko alexey.kirichenko@gmail.com",
|
|
47
48
|
"license": "MIT",
|
|
48
49
|
"repository": {
|
|
49
50
|
"type": "git",
|