jattac.libs.web.zest-button 1.1.0 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs.js +66 -40
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.esm.js +66 -40
- package/dist/index.esm.js.map +1 -1
- package/package.json +2 -1
package/dist/index.esm.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import require$$0, { useState, useRef
|
|
2
|
-
import { FaSpinner } from 'react-icons/
|
|
1
|
+
import require$$0, { useEffect, useState, useRef } from 'react';
|
|
2
|
+
import { FaSpinner } from 'react-icons/fa6';
|
|
3
3
|
|
|
4
4
|
var jsxRuntime = {exports: {}};
|
|
5
5
|
|
|
@@ -1394,10 +1394,22 @@ const ZestButton = ({ visualOptions = {}, busyOptions = {}, successOptions = {},
|
|
|
1394
1394
|
const { variant = "standard", size = "md", fullWidth = false, iconLeft, iconRight, } = visualOptions;
|
|
1395
1395
|
const { handleInternally = true, preventRageClick = true, minBusyDurationMs = 500, } = busyOptions;
|
|
1396
1396
|
const { showCheckmark = true, showFailIcon = true, autoResetAfterMs = 2000, } = successOptions;
|
|
1397
|
+
// Edge Case 4: Add warnings for very short durations in development
|
|
1398
|
+
useEffect(() => {
|
|
1399
|
+
if (process.env.NODE_ENV === 'development') {
|
|
1400
|
+
if (minBusyDurationMs > 0 && minBusyDurationMs < 100) {
|
|
1401
|
+
console.warn(`ZestButton: 'minBusyDurationMs' is set to ${minBusyDurationMs}ms. Very short durations can lead to visual flickering and a poor user experience. Consider a value of 100ms or more.`);
|
|
1402
|
+
}
|
|
1403
|
+
if (autoResetAfterMs > 0 && autoResetAfterMs < 100) {
|
|
1404
|
+
console.warn(`ZestButton: 'autoResetAfterMs' is set to ${autoResetAfterMs}ms. Very short durations can make success/fail feedback hard to perceive. Consider a value of 100ms or more.`);
|
|
1405
|
+
}
|
|
1406
|
+
}
|
|
1407
|
+
}, [minBusyDurationMs, autoResetAfterMs]);
|
|
1397
1408
|
const [internalBusy, setInternalBusy] = useState(false);
|
|
1398
1409
|
const [wasSuccessful, setWasSuccessful] = useState(false);
|
|
1399
1410
|
const [wasFailed, setWasFailed] = useState(false);
|
|
1400
1411
|
const buttonRef = useRef(null);
|
|
1412
|
+
const failTimeoutRef = useRef(null); // Bug 1: Add useRef for failTimeout
|
|
1401
1413
|
const [currentChildren, setCurrentChildren] = useState(children);
|
|
1402
1414
|
const [awaitingConfirm, setAwaitingConfirm] = useState(false);
|
|
1403
1415
|
// ✅ interval ref for confirm countdown
|
|
@@ -1416,6 +1428,15 @@ const ZestButton = ({ visualOptions = {}, busyOptions = {}, successOptions = {},
|
|
|
1416
1428
|
const isDisabled = disabled ||
|
|
1417
1429
|
effectiveBusy ||
|
|
1418
1430
|
(preventRageClick && (wasSuccessful || wasFailed));
|
|
1431
|
+
// Move handleClick, stopWaiting, and handleConfirmClick declarations here
|
|
1432
|
+
const stopWaiting = () => {
|
|
1433
|
+
if (confirmIntervalRef.current) {
|
|
1434
|
+
clearInterval(confirmIntervalRef.current);
|
|
1435
|
+
confirmIntervalRef.current = null;
|
|
1436
|
+
}
|
|
1437
|
+
setCurrentChildren(children);
|
|
1438
|
+
setAwaitingConfirm(false);
|
|
1439
|
+
};
|
|
1419
1440
|
const handleClick = async (e) => {
|
|
1420
1441
|
if (preventRageClick && internalBusy)
|
|
1421
1442
|
return;
|
|
@@ -1447,45 +1468,14 @@ const ZestButton = ({ visualOptions = {}, busyOptions = {}, successOptions = {},
|
|
|
1447
1468
|
onClick(e);
|
|
1448
1469
|
}
|
|
1449
1470
|
};
|
|
1450
|
-
// auto-reset success/failure state
|
|
1451
|
-
useEffect(() => {
|
|
1452
|
-
if ((wasSuccessful || wasFailed) && autoResetAfterMs) {
|
|
1453
|
-
const timeout = setTimeout(() => {
|
|
1454
|
-
setWasSuccessful(false);
|
|
1455
|
-
setWasFailed(false);
|
|
1456
|
-
}, autoResetAfterMs);
|
|
1457
|
-
return () => clearTimeout(timeout);
|
|
1458
|
-
}
|
|
1459
|
-
}, [wasSuccessful, wasFailed, autoResetAfterMs]);
|
|
1460
|
-
// Enter key handler if isDefault
|
|
1461
|
-
useEffect(() => {
|
|
1462
|
-
if (!isDefault || isDisabled)
|
|
1463
|
-
return;
|
|
1464
|
-
const listener = (e) => {
|
|
1465
|
-
const target = e.target;
|
|
1466
|
-
if (e.key === "Enter" &&
|
|
1467
|
-
!e.repeat &&
|
|
1468
|
-
!e.defaultPrevented &&
|
|
1469
|
-
!(target instanceof HTMLTextAreaElement)) {
|
|
1470
|
-
e.preventDefault();
|
|
1471
|
-
buttonRef.current?.click();
|
|
1472
|
-
}
|
|
1473
|
-
};
|
|
1474
|
-
document.addEventListener("keydown", listener);
|
|
1475
|
-
return () => document.removeEventListener("keydown", listener);
|
|
1476
|
-
}, [isDefault, isDisabled]);
|
|
1477
|
-
const stopWaiting = () => {
|
|
1478
|
-
if (confirmIntervalRef.current) {
|
|
1479
|
-
clearInterval(confirmIntervalRef.current);
|
|
1480
|
-
confirmIntervalRef.current = null;
|
|
1481
|
-
}
|
|
1482
|
-
setCurrentChildren(children);
|
|
1483
|
-
setAwaitingConfirm(false);
|
|
1484
|
-
};
|
|
1485
1471
|
const handleConfirmClick = async (e) => {
|
|
1486
1472
|
if (!props.confirmOptions) {
|
|
1487
1473
|
return handleClick(e);
|
|
1488
1474
|
}
|
|
1475
|
+
// Edge Case 3: Add warning for missing onClick with confirmOptions
|
|
1476
|
+
if (props.confirmOptions && !onClick) { // Use destructured onClick
|
|
1477
|
+
console.warn("ZestButton: 'confirmOptions' are provided but 'onClick' handler is missing. The button will confirm but perform no action.");
|
|
1478
|
+
}
|
|
1489
1479
|
if (awaitingConfirm) {
|
|
1490
1480
|
stopWaiting();
|
|
1491
1481
|
return handleClick(e);
|
|
@@ -1500,22 +1490,58 @@ const ZestButton = ({ visualOptions = {}, busyOptions = {}, successOptions = {},
|
|
|
1500
1490
|
if (timeRemaining <= 0) {
|
|
1501
1491
|
stopWaiting();
|
|
1502
1492
|
setWasFailed(true); // Indicate failure for shake animation
|
|
1503
|
-
|
|
1493
|
+
// Bug 1: Clear any existing timeout before setting a new one
|
|
1494
|
+
if (failTimeoutRef.current) {
|
|
1495
|
+
clearTimeout(failTimeoutRef.current);
|
|
1496
|
+
}
|
|
1497
|
+
failTimeoutRef.current = setTimeout(() => {
|
|
1504
1498
|
setWasFailed(false);
|
|
1499
|
+
failTimeoutRef.current = null; // Clear ref after timeout fires
|
|
1505
1500
|
}, 400); // Shake animation duration
|
|
1506
|
-
// No need to return clearTimeout, as this timeout is for visual feedback only
|
|
1507
1501
|
}
|
|
1508
1502
|
else {
|
|
1509
1503
|
setCurrentChildren(`${displayLabel} (${timeRemaining}s)`);
|
|
1510
1504
|
}
|
|
1511
1505
|
}, 1000); // Update once per second
|
|
1512
1506
|
};
|
|
1507
|
+
// auto-reset success/failure state
|
|
1508
|
+
useEffect(() => {
|
|
1509
|
+
if ((wasSuccessful || wasFailed) && autoResetAfterMs) {
|
|
1510
|
+
const timeout = setTimeout(() => {
|
|
1511
|
+
setWasSuccessful(false);
|
|
1512
|
+
setWasFailed(false);
|
|
1513
|
+
}, autoResetAfterMs);
|
|
1514
|
+
return () => clearTimeout(timeout);
|
|
1515
|
+
}
|
|
1516
|
+
}, [wasSuccessful, wasFailed, autoResetAfterMs]);
|
|
1517
|
+
// Enter key handler if isDefault
|
|
1518
|
+
useEffect(() => {
|
|
1519
|
+
if (!isDefault || isDisabled)
|
|
1520
|
+
return;
|
|
1521
|
+
const listener = (e) => {
|
|
1522
|
+
const target = e.target;
|
|
1523
|
+
if (e.key === "Enter" &&
|
|
1524
|
+
!e.repeat &&
|
|
1525
|
+
!e.defaultPrevented &&
|
|
1526
|
+
!(target instanceof HTMLTextAreaElement)) {
|
|
1527
|
+
e.preventDefault();
|
|
1528
|
+
// Bug 2: Directly call handleConfirmClick to respect confirmation logic
|
|
1529
|
+
handleConfirmClick(e);
|
|
1530
|
+
}
|
|
1531
|
+
};
|
|
1532
|
+
document.addEventListener("keydown", listener);
|
|
1533
|
+
return () => document.removeEventListener("keydown", listener);
|
|
1534
|
+
}, [isDefault, isDisabled, handleConfirmClick]); // Bug 2: Add handleConfirmClick to dependencies
|
|
1513
1535
|
// cleanup on unmount
|
|
1514
1536
|
useEffect(() => {
|
|
1515
1537
|
return () => {
|
|
1516
1538
|
if (confirmIntervalRef.current) {
|
|
1517
1539
|
clearInterval(confirmIntervalRef.current);
|
|
1518
1540
|
}
|
|
1541
|
+
// Bug 1: Add cleanup for failTimeoutRef
|
|
1542
|
+
if (failTimeoutRef.current) {
|
|
1543
|
+
clearTimeout(failTimeoutRef.current);
|
|
1544
|
+
}
|
|
1519
1545
|
};
|
|
1520
1546
|
}, []);
|
|
1521
1547
|
const renderLeftIcon = () => {
|
|
@@ -1526,7 +1552,7 @@ const ZestButton = ({ visualOptions = {}, busyOptions = {}, successOptions = {},
|
|
|
1526
1552
|
return (jsxRuntimeExports.jsx("span", { className: `${styles.icon} ${styles.fadeIn}`, children: jsxRuntimeExports.jsx(AnimatedCheckmark, {}) }));
|
|
1527
1553
|
}
|
|
1528
1554
|
else if (wasFailed && showFailIcon) {
|
|
1529
|
-
return (jsxRuntimeExports.jsx("span", { className: `${styles.icon} ${styles.fadeIn}
|
|
1555
|
+
return (jsxRuntimeExports.jsx("span", { className: `${styles.icon} ${styles.fadeIn}`, children: jsxRuntimeExports.jsx(AnimatedX, {}) }));
|
|
1530
1556
|
}
|
|
1531
1557
|
else if (iconLeft) {
|
|
1532
1558
|
return jsxRuntimeExports.jsx("span", { className: styles.icon, children: iconLeft });
|