jattac.libs.web.zest-button 1.2.1 → 1.2.3
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 -325
- package/dist/ZestButton.d.ts +6 -2
- package/dist/ZestContext.d.ts +9 -0
- package/dist/ZestProvider.d.ts +8 -0
- package/dist/hooks/useBusyState.d.ts +20 -0
- package/dist/hooks/useConfirmation.d.ts +13 -0
- package/dist/hooks/useThemeDetection.d.ts +1 -0
- package/dist/hooks/useZestConfig.d.ts +2 -0
- package/dist/index.cjs.js +376 -110
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +18 -2
- package/dist/index.esm.js +375 -111
- package/dist/index.esm.js.map +1 -1
- package/dist/semanticTypeDefaults.d.ts +4 -0
- package/docs/api.md +119 -0
- package/docs/breaking-changes.md +52 -0
- package/docs/configuration.md +192 -0
- package/docs/development.md +77 -0
- package/docs/examples.md +215 -0
- package/docs/features.md +113 -0
- package/package.json +24 -7
package/dist/index.esm.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import require$$0, {
|
|
1
|
+
import require$$0, { useContext, useState, useRef, useEffect, useCallback } from 'react';
|
|
2
2
|
import { FaSpinner } from 'react-icons/fa6';
|
|
3
|
+
import { FaShareAlt, FaPrint, FaSyncAlt, FaUpload, FaDownload, FaArrowRight, FaTimes, FaTrash, FaEdit, FaSave, FaPlus } from 'react-icons/fa';
|
|
3
4
|
|
|
4
5
|
var jsxRuntime = {exports: {}};
|
|
5
6
|
|
|
@@ -1387,18 +1388,218 @@ var css_248z = "/* Styles/ZestButton.module.css */\n\n/* Define base variables f
|
|
|
1387
1388
|
var styles = {"force-light":"ZestButton-module_force-light__zZTIZ","force-dark":"ZestButton-module_force-dark__cx74D","button":"ZestButton-module_button__KDafc","solid":"ZestButton-module_solid__cu4tr","outline":"ZestButton-module_outline__esgLq","text":"ZestButton-module_text__8X1xD","dashed":"ZestButton-module_dashed__ebKYF","standard":"ZestButton-module_standard__T3EGM","success":"ZestButton-module_success__XEptA","danger":"ZestButton-module_danger__nJpJ-","disabled":"ZestButton-module_disabled__gw6y3","fullWidth":"ZestButton-module_fullWidth__2ziwk","sm":"ZestButton-module_sm__G1vAP","md":"ZestButton-module_md__Y-PMO","lg":"ZestButton-module_lg__AQgdf","inner":"ZestButton-module_inner__1j2Fr","spinner":"ZestButton-module_spinner__l2hLe","spin":"ZestButton-module_spin__4asdw","content":"ZestButton-module_content__hlea3","label":"ZestButton-module_label__8x263","icon":"ZestButton-module_icon__B3DFi","animatedCheck":"ZestButton-module_animatedCheck__8K4K-","fadeIn":"ZestButton-module_fadeIn__iEave","animatedX":"ZestButton-module_animatedX__KQnt7","drawCheck":"ZestButton-module_drawCheck__3DyjT","shake":"ZestButton-module_shake__NtIjf","shakeIt":"ZestButton-module_shakeIt__ox-R3"};
|
|
1388
1389
|
styleInject(css_248z);
|
|
1389
1390
|
|
|
1390
|
-
//
|
|
1391
|
-
const
|
|
1392
|
-
|
|
1393
|
-
const
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
//
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1391
|
+
// Create the Zest Context with a default empty configuration
|
|
1392
|
+
const ZestContext = require$$0.createContext(undefined);
|
|
1393
|
+
// Custom hook to use the Zest Context
|
|
1394
|
+
const useZest = () => {
|
|
1395
|
+
const context = useContext(ZestContext);
|
|
1396
|
+
// Optional: Add a check here if context is undefined, meaning provider is not used
|
|
1397
|
+
// if (context === undefined) {
|
|
1398
|
+
// console.warn("useZest must be used within a ZestProvider. Falling back to default ZestButton props.");
|
|
1399
|
+
// }
|
|
1400
|
+
return context;
|
|
1401
|
+
};
|
|
1402
|
+
|
|
1403
|
+
const semanticTypeDefaults = {
|
|
1404
|
+
// Creation / Add
|
|
1405
|
+
'add': {
|
|
1406
|
+
visualOptions: {
|
|
1407
|
+
variant: 'standard',
|
|
1408
|
+
iconLeft: jsxRuntimeExports.jsx(FaPlus, {}),
|
|
1409
|
+
},
|
|
1410
|
+
},
|
|
1411
|
+
'new': {
|
|
1412
|
+
visualOptions: {
|
|
1413
|
+
variant: 'standard',
|
|
1414
|
+
iconLeft: jsxRuntimeExports.jsx(FaPlus, {}),
|
|
1415
|
+
},
|
|
1416
|
+
},
|
|
1417
|
+
// Save / Submit
|
|
1418
|
+
'save': {
|
|
1419
|
+
visualOptions: {
|
|
1420
|
+
variant: 'success',
|
|
1421
|
+
iconLeft: jsxRuntimeExports.jsx(FaSave, {}),
|
|
1422
|
+
},
|
|
1423
|
+
busyOptions: { minBusyDurationMs: 500 }, // Default busy duration for saves
|
|
1424
|
+
successOptions: { showCheckmark: true },
|
|
1425
|
+
},
|
|
1426
|
+
'submit': {
|
|
1427
|
+
visualOptions: {
|
|
1428
|
+
variant: 'success',
|
|
1429
|
+
iconLeft: jsxRuntimeExports.jsx(FaSave, {}),
|
|
1430
|
+
},
|
|
1431
|
+
busyOptions: { minBusyDurationMs: 500 },
|
|
1432
|
+
successOptions: { showCheckmark: true },
|
|
1433
|
+
},
|
|
1434
|
+
// Edit / Update
|
|
1435
|
+
'edit': {
|
|
1436
|
+
visualOptions: {
|
|
1437
|
+
variant: 'standard',
|
|
1438
|
+
iconLeft: jsxRuntimeExports.jsx(FaEdit, {}),
|
|
1439
|
+
},
|
|
1440
|
+
},
|
|
1441
|
+
'update': {
|
|
1442
|
+
visualOptions: {
|
|
1443
|
+
variant: 'standard',
|
|
1444
|
+
iconLeft: jsxRuntimeExports.jsx(FaEdit, {}),
|
|
1445
|
+
},
|
|
1446
|
+
},
|
|
1447
|
+
// Delete / Remove
|
|
1448
|
+
'delete': {
|
|
1449
|
+
visualOptions: {
|
|
1450
|
+
variant: 'danger',
|
|
1451
|
+
iconLeft: jsxRuntimeExports.jsx(FaTrash, {}),
|
|
1452
|
+
},
|
|
1453
|
+
confirmOptions: {
|
|
1454
|
+
displayLabel: 'Confirm Delete',
|
|
1455
|
+
timeoutSecs: 5,
|
|
1456
|
+
},
|
|
1457
|
+
successOptions: { showFailIcon: true, autoResetAfterMs: 400 }, // Shake for fail
|
|
1458
|
+
},
|
|
1459
|
+
'remove': {
|
|
1460
|
+
visualOptions: {
|
|
1461
|
+
variant: 'danger',
|
|
1462
|
+
iconLeft: jsxRuntimeExports.jsx(FaTrash, {}),
|
|
1463
|
+
},
|
|
1464
|
+
confirmOptions: {
|
|
1465
|
+
displayLabel: 'Confirm Remove',
|
|
1466
|
+
timeoutSecs: 5,
|
|
1467
|
+
},
|
|
1468
|
+
successOptions: { showFailIcon: true, autoResetAfterMs: 400 },
|
|
1469
|
+
},
|
|
1470
|
+
// Cancel / Close
|
|
1471
|
+
'cancel': {
|
|
1472
|
+
buttonStyle: 'outline', // Moved here
|
|
1473
|
+
visualOptions: {
|
|
1474
|
+
variant: 'standard',
|
|
1475
|
+
iconLeft: jsxRuntimeExports.jsx(FaTimes, {}),
|
|
1476
|
+
},
|
|
1477
|
+
},
|
|
1478
|
+
'close': {
|
|
1479
|
+
buttonStyle: 'outline', // Moved here
|
|
1480
|
+
visualOptions: {
|
|
1481
|
+
variant: 'standard',
|
|
1482
|
+
iconLeft: jsxRuntimeExports.jsx(FaTimes, {}),
|
|
1483
|
+
},
|
|
1484
|
+
},
|
|
1485
|
+
// View / Details
|
|
1486
|
+
'view': {
|
|
1487
|
+
buttonStyle: 'text', // Moved here
|
|
1488
|
+
visualOptions: {
|
|
1489
|
+
variant: 'standard',
|
|
1490
|
+
iconRight: jsxRuntimeExports.jsx(FaArrowRight, {}),
|
|
1491
|
+
},
|
|
1492
|
+
},
|
|
1493
|
+
'details': {
|
|
1494
|
+
buttonStyle: 'text', // Moved here
|
|
1495
|
+
visualOptions: {
|
|
1496
|
+
variant: 'standard',
|
|
1497
|
+
iconRight: jsxRuntimeExports.jsx(FaArrowRight, {}),
|
|
1498
|
+
},
|
|
1499
|
+
},
|
|
1500
|
+
// Download
|
|
1501
|
+
'download': {
|
|
1502
|
+
visualOptions: {
|
|
1503
|
+
variant: 'standard',
|
|
1504
|
+
iconLeft: jsxRuntimeExports.jsx(FaDownload, {}),
|
|
1505
|
+
},
|
|
1506
|
+
},
|
|
1507
|
+
// Upload
|
|
1508
|
+
'upload': {
|
|
1509
|
+
visualOptions: {
|
|
1510
|
+
variant: 'standard',
|
|
1511
|
+
iconLeft: jsxRuntimeExports.jsx(FaUpload, {}),
|
|
1512
|
+
},
|
|
1513
|
+
},
|
|
1514
|
+
// Refresh / Reload
|
|
1515
|
+
'refresh': {
|
|
1516
|
+
visualOptions: {
|
|
1517
|
+
variant: 'standard',
|
|
1518
|
+
iconLeft: jsxRuntimeExports.jsx(FaSyncAlt, {}),
|
|
1519
|
+
},
|
|
1520
|
+
busyOptions: { minBusyDurationMs: 500 },
|
|
1521
|
+
},
|
|
1522
|
+
'reload': {
|
|
1523
|
+
visualOptions: {
|
|
1524
|
+
variant: 'standard',
|
|
1525
|
+
iconLeft: jsxRuntimeExports.jsx(FaSyncAlt, {}),
|
|
1526
|
+
},
|
|
1527
|
+
busyOptions: { minBusyDurationMs: 500 },
|
|
1528
|
+
},
|
|
1529
|
+
// Print
|
|
1530
|
+
'print': {
|
|
1531
|
+
visualOptions: {
|
|
1532
|
+
variant: 'standard',
|
|
1533
|
+
iconLeft: jsxRuntimeExports.jsx(FaPrint, {}),
|
|
1534
|
+
},
|
|
1535
|
+
},
|
|
1536
|
+
// Share
|
|
1537
|
+
'share': {
|
|
1538
|
+
visualOptions: {
|
|
1539
|
+
variant: 'standard',
|
|
1540
|
+
iconLeft: jsxRuntimeExports.jsx(FaShareAlt, {}),
|
|
1541
|
+
},
|
|
1542
|
+
},
|
|
1543
|
+
// Confirm (generic)
|
|
1544
|
+
'confirm': {
|
|
1545
|
+
visualOptions: {
|
|
1546
|
+
variant: 'success', // Could be standard or success
|
|
1547
|
+
},
|
|
1548
|
+
confirmOptions: {
|
|
1549
|
+
displayLabel: 'Are you sure?',
|
|
1550
|
+
timeoutSecs: 5,
|
|
1551
|
+
},
|
|
1552
|
+
},
|
|
1553
|
+
};
|
|
1554
|
+
|
|
1555
|
+
// Define a deep merge utility function
|
|
1556
|
+
const deepMerge = (target, source) => {
|
|
1557
|
+
const output = { ...target };
|
|
1558
|
+
if (target && typeof target === 'object' && source && typeof source === 'object') {
|
|
1559
|
+
Object.keys(source).forEach(key => {
|
|
1560
|
+
if (source[key] && typeof source[key] === 'object' && !Array.isArray(source[key])) {
|
|
1561
|
+
if (!(key in target))
|
|
1562
|
+
Object.assign(output, { [key]: source[key] });
|
|
1563
|
+
else
|
|
1564
|
+
output[key] = deepMerge(target[key], source[key]);
|
|
1565
|
+
}
|
|
1566
|
+
else {
|
|
1567
|
+
Object.assign(output, { [key]: source[key] });
|
|
1568
|
+
}
|
|
1569
|
+
});
|
|
1570
|
+
}
|
|
1571
|
+
return output;
|
|
1572
|
+
};
|
|
1573
|
+
const useZestConfig = (localZestProps) => {
|
|
1574
|
+
const globalConfig = useZest();
|
|
1575
|
+
const globalDefaultProps = globalConfig?.defaultProps;
|
|
1576
|
+
const customSemanticDefaults = globalConfig?.semanticTypeDefaults;
|
|
1577
|
+
// 1. Start with global defaults (lowest precedence)
|
|
1578
|
+
let effectiveZestConfig = deepMerge({}, globalDefaultProps || {});
|
|
1579
|
+
// Determine the semanticType from either local props or the already-merged global props
|
|
1580
|
+
const semanticType = localZestProps?.semanticType || effectiveZestConfig.semanticType;
|
|
1581
|
+
if (semanticType) {
|
|
1582
|
+
// 2. Apply built-in semantic defaults
|
|
1583
|
+
if (semanticTypeDefaults[semanticType]) {
|
|
1584
|
+
effectiveZestConfig = deepMerge(effectiveZestConfig, semanticTypeDefaults[semanticType]);
|
|
1585
|
+
}
|
|
1586
|
+
// 3. Apply custom semantic defaults from provider (overrides built-in ones)
|
|
1587
|
+
if (customSemanticDefaults && customSemanticDefaults[semanticType]) {
|
|
1588
|
+
effectiveZestConfig = deepMerge(effectiveZestConfig, customSemanticDefaults[semanticType]);
|
|
1589
|
+
}
|
|
1590
|
+
}
|
|
1591
|
+
// 4. Apply local props (highest precedence)
|
|
1592
|
+
effectiveZestConfig = deepMerge(effectiveZestConfig, localZestProps || {});
|
|
1593
|
+
return effectiveZestConfig;
|
|
1594
|
+
};
|
|
1595
|
+
|
|
1596
|
+
const useBusyState = ({ busyOptions, successOptions }) => {
|
|
1597
|
+
const [internalBusy, setInternalBusy] = useState(false);
|
|
1598
|
+
const [wasSuccessful, setWasSuccessful] = useState(false);
|
|
1599
|
+
const [wasFailed, setWasFailed] = useState(false);
|
|
1600
|
+
const failTimeoutRef = useRef(null);
|
|
1601
|
+
const { handleInternally = true, preventRageClick = true, minBusyDurationMs = 500, } = busyOptions || {};
|
|
1602
|
+
const { showCheckmark = true, showFailIcon = true, autoResetAfterMs = 2000, } = successOptions || {};
|
|
1402
1603
|
// Edge Case 4: Add warnings for very short durations in development
|
|
1403
1604
|
useEffect(() => {
|
|
1404
1605
|
if (process.env.NODE_ENV === 'development') {
|
|
@@ -1410,16 +1611,120 @@ zest, // New parent prop
|
|
|
1410
1611
|
}
|
|
1411
1612
|
}
|
|
1412
1613
|
}, [minBusyDurationMs, autoResetAfterMs]);
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1614
|
+
// auto-reset success/failure state
|
|
1615
|
+
useEffect(() => {
|
|
1616
|
+
if ((wasSuccessful || wasFailed) && autoResetAfterMs) {
|
|
1617
|
+
const timeout = setTimeout(() => {
|
|
1618
|
+
setWasSuccessful(false);
|
|
1619
|
+
setWasFailed(false);
|
|
1620
|
+
if (failTimeoutRef.current) { // Clear fail timeout if still active
|
|
1621
|
+
clearTimeout(failTimeoutRef.current);
|
|
1622
|
+
failTimeoutRef.current = null;
|
|
1623
|
+
}
|
|
1624
|
+
}, autoResetAfterMs);
|
|
1625
|
+
return () => clearTimeout(timeout);
|
|
1626
|
+
}
|
|
1627
|
+
}, [wasSuccessful, wasFailed, autoResetAfterMs]);
|
|
1628
|
+
// Cleanup for failTimeoutRef on unmount
|
|
1629
|
+
useEffect(() => {
|
|
1630
|
+
return () => {
|
|
1631
|
+
if (failTimeoutRef.current) {
|
|
1632
|
+
clearTimeout(failTimeoutRef.current);
|
|
1633
|
+
}
|
|
1634
|
+
};
|
|
1635
|
+
}, []);
|
|
1636
|
+
const startBusy = () => {
|
|
1637
|
+
setWasSuccessful(false);
|
|
1638
|
+
setWasFailed(false);
|
|
1639
|
+
setInternalBusy(true);
|
|
1640
|
+
};
|
|
1641
|
+
const endBusy = (isSuccess) => {
|
|
1642
|
+
setInternalBusy(false);
|
|
1643
|
+
if (isSuccess) {
|
|
1644
|
+
if (showCheckmark)
|
|
1645
|
+
setWasSuccessful(true);
|
|
1646
|
+
}
|
|
1647
|
+
else {
|
|
1648
|
+
if (showFailIcon)
|
|
1649
|
+
setWasFailed(true);
|
|
1650
|
+
// Bug 1: Clear any existing timeout before setting a new one
|
|
1651
|
+
if (failTimeoutRef.current) {
|
|
1652
|
+
clearTimeout(failTimeoutRef.current);
|
|
1653
|
+
}
|
|
1654
|
+
failTimeoutRef.current = setTimeout(() => {
|
|
1655
|
+
setWasFailed(false);
|
|
1656
|
+
failTimeoutRef.current = null; // Clear ref after timeout fires
|
|
1657
|
+
}, 400); // Shake animation duration
|
|
1658
|
+
}
|
|
1659
|
+
};
|
|
1660
|
+
return {
|
|
1661
|
+
internalBusy,
|
|
1662
|
+
wasSuccessful,
|
|
1663
|
+
wasFailed,
|
|
1664
|
+
startBusy,
|
|
1665
|
+
endBusy,
|
|
1666
|
+
handleInternally,
|
|
1667
|
+
preventRageClick,
|
|
1668
|
+
minBusyDurationMs,
|
|
1669
|
+
showCheckmark,
|
|
1670
|
+
showFailIcon,
|
|
1671
|
+
autoResetAfterMs,
|
|
1672
|
+
failTimeoutRef // Expose this for ZestButton to clear if needed (e.g., confirmation failure)
|
|
1673
|
+
};
|
|
1674
|
+
};
|
|
1675
|
+
|
|
1676
|
+
const useConfirmation = ({ confirmOptions, originalChildren, onConfirmFail }) => {
|
|
1419
1677
|
const [awaitingConfirm, setAwaitingConfirm] = useState(false);
|
|
1420
|
-
|
|
1678
|
+
const [currentChildren, setCurrentChildren] = useState(originalChildren);
|
|
1421
1679
|
const confirmIntervalRef = useRef(null);
|
|
1422
|
-
|
|
1680
|
+
useEffect(() => {
|
|
1681
|
+
if (!awaitingConfirm) {
|
|
1682
|
+
setCurrentChildren(originalChildren);
|
|
1683
|
+
}
|
|
1684
|
+
}, [originalChildren, awaitingConfirm]);
|
|
1685
|
+
const stopConfirmation = useCallback(() => {
|
|
1686
|
+
if (confirmIntervalRef.current) {
|
|
1687
|
+
clearInterval(confirmIntervalRef.current);
|
|
1688
|
+
confirmIntervalRef.current = null;
|
|
1689
|
+
}
|
|
1690
|
+
setCurrentChildren(originalChildren);
|
|
1691
|
+
setAwaitingConfirm(false);
|
|
1692
|
+
}, [originalChildren]);
|
|
1693
|
+
const startConfirmation = useCallback(() => {
|
|
1694
|
+
if (!confirmOptions)
|
|
1695
|
+
return;
|
|
1696
|
+
setAwaitingConfirm(true);
|
|
1697
|
+
const { displayLabel, timeoutSecs } = confirmOptions;
|
|
1698
|
+
setCurrentChildren(`${displayLabel} (${timeoutSecs}s)`);
|
|
1699
|
+
const startTime = Date.now();
|
|
1700
|
+
confirmIntervalRef.current = setInterval(() => {
|
|
1701
|
+
const elapsed = Date.now() - startTime;
|
|
1702
|
+
const timeRemaining = timeoutSecs - Math.floor(elapsed / 1000);
|
|
1703
|
+
if (timeRemaining <= 0) {
|
|
1704
|
+
stopConfirmation();
|
|
1705
|
+
onConfirmFail?.();
|
|
1706
|
+
}
|
|
1707
|
+
else {
|
|
1708
|
+
setCurrentChildren(`${displayLabel} (${timeRemaining}s)`);
|
|
1709
|
+
}
|
|
1710
|
+
}, 1000);
|
|
1711
|
+
}, [confirmOptions, onConfirmFail, stopConfirmation]);
|
|
1712
|
+
useEffect(() => {
|
|
1713
|
+
return () => {
|
|
1714
|
+
if (confirmIntervalRef.current) {
|
|
1715
|
+
clearInterval(confirmIntervalRef.current);
|
|
1716
|
+
}
|
|
1717
|
+
};
|
|
1718
|
+
}, []);
|
|
1719
|
+
return {
|
|
1720
|
+
awaitingConfirm,
|
|
1721
|
+
currentChildren,
|
|
1722
|
+
startConfirmation,
|
|
1723
|
+
stopConfirmation,
|
|
1724
|
+
};
|
|
1725
|
+
};
|
|
1726
|
+
|
|
1727
|
+
const useThemeDetection = () => {
|
|
1423
1728
|
const [systemTheme, setSystemTheme] = useState('light');
|
|
1424
1729
|
useEffect(() => {
|
|
1425
1730
|
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
|
|
@@ -1430,13 +1735,25 @@ zest, // New parent prop
|
|
|
1430
1735
|
mediaQuery.addEventListener('change', handleChange);
|
|
1431
1736
|
return () => mediaQuery.removeEventListener('change', handleChange);
|
|
1432
1737
|
}, []);
|
|
1738
|
+
return systemTheme;
|
|
1739
|
+
};
|
|
1740
|
+
|
|
1741
|
+
// --- Components ---
|
|
1742
|
+
const AnimatedCheckmark = () => (jsxRuntimeExports.jsx("svg", { className: styles.animatedCheck, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "3", strokeLinecap: "round", strokeLinejoin: "round", children: jsxRuntimeExports.jsx("path", { d: "M5 13l4 4L19 7" }) }));
|
|
1743
|
+
const AnimatedX = () => (jsxRuntimeExports.jsxs("svg", { className: styles.animatedX, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "3", strokeLinecap: "round", strokeLinejoin: "round", children: [jsxRuntimeExports.jsx("path", { d: "M6 6L18 18" }), jsxRuntimeExports.jsx("path", { d: "M6 18L18 6" })] }));
|
|
1744
|
+
const ZestButton = ({ className = "", disabled, children, onClick, zest: localZestProps, ...props }) => {
|
|
1745
|
+
const effectiveZestConfig = useZestConfig(localZestProps);
|
|
1746
|
+
const { visualOptions = {}, busyOptions = {}, successOptions = {}, confirmOptions, isDefault = false, theme = 'system', buttonStyle = 'solid', } = effectiveZestConfig;
|
|
1747
|
+
const { variant = "standard", size = "md", fullWidth = false, iconLeft, iconRight, } = visualOptions;
|
|
1748
|
+
const { handleInternally, minBusyDurationMs, preventRageClick, showCheckmark, showFailIcon, internalBusy, wasSuccessful, wasFailed, startBusy, endBusy, } = useBusyState({ busyOptions, successOptions });
|
|
1749
|
+
const { awaitingConfirm, currentChildren, startConfirmation, stopConfirmation, } = useConfirmation({
|
|
1750
|
+
confirmOptions,
|
|
1751
|
+
originalChildren: children,
|
|
1752
|
+
onConfirmFail: () => endBusy(false),
|
|
1753
|
+
});
|
|
1754
|
+
const buttonRef = useRef(null);
|
|
1755
|
+
const systemTheme = useThemeDetection();
|
|
1433
1756
|
const effectiveTheme = theme === 'system' ? systemTheme : theme;
|
|
1434
|
-
// keep children in sync when not in confirm mode
|
|
1435
|
-
useEffect(() => {
|
|
1436
|
-
if (!awaitingConfirm) {
|
|
1437
|
-
setCurrentChildren(children);
|
|
1438
|
-
}
|
|
1439
|
-
}, [children, awaitingConfirm]);
|
|
1440
1757
|
const effectiveBusy = typeof props["aria-busy"] === "boolean"
|
|
1441
1758
|
? Boolean(props["aria-busy"])
|
|
1442
1759
|
: handleInternally
|
|
@@ -1445,23 +1762,12 @@ zest, // New parent prop
|
|
|
1445
1762
|
const isDisabled = disabled ||
|
|
1446
1763
|
effectiveBusy ||
|
|
1447
1764
|
(preventRageClick && (wasSuccessful || wasFailed));
|
|
1448
|
-
// Move handleClick, stopWaiting, and handleConfirmClick declarations here
|
|
1449
|
-
const stopWaiting = () => {
|
|
1450
|
-
if (confirmIntervalRef.current) {
|
|
1451
|
-
clearInterval(confirmIntervalRef.current);
|
|
1452
|
-
confirmIntervalRef.current = null;
|
|
1453
|
-
}
|
|
1454
|
-
setCurrentChildren(children);
|
|
1455
|
-
setAwaitingConfirm(false);
|
|
1456
|
-
};
|
|
1457
1765
|
const handleClick = async (e) => {
|
|
1458
1766
|
if (preventRageClick && internalBusy)
|
|
1459
1767
|
return;
|
|
1460
1768
|
if (handleInternally && typeof onClick === "function") {
|
|
1461
1769
|
try {
|
|
1462
|
-
|
|
1463
|
-
setWasFailed(false);
|
|
1464
|
-
setInternalBusy(true);
|
|
1770
|
+
startBusy();
|
|
1465
1771
|
const startTime = Date.now();
|
|
1466
1772
|
await onClick(e);
|
|
1467
1773
|
const elapsed = Date.now() - startTime;
|
|
@@ -1469,69 +1775,33 @@ zest, // New parent prop
|
|
|
1469
1775
|
if (remaining > 0) {
|
|
1470
1776
|
await new Promise((resolve) => setTimeout(resolve, remaining));
|
|
1471
1777
|
}
|
|
1472
|
-
|
|
1473
|
-
setWasSuccessful(true);
|
|
1778
|
+
endBusy(true);
|
|
1474
1779
|
}
|
|
1475
1780
|
catch (err) {
|
|
1476
1781
|
console.error(err);
|
|
1477
|
-
|
|
1478
|
-
setWasFailed(true);
|
|
1479
|
-
}
|
|
1480
|
-
finally {
|
|
1481
|
-
setInternalBusy(false);
|
|
1782
|
+
endBusy(false);
|
|
1482
1783
|
}
|
|
1483
1784
|
}
|
|
1484
1785
|
else if (onClick) {
|
|
1485
1786
|
onClick(e);
|
|
1486
1787
|
}
|
|
1487
1788
|
};
|
|
1488
|
-
const handleConfirmClick =
|
|
1489
|
-
if (!confirmOptions) { // Use destructured confirmOptions
|
|
1490
|
-
return handleClick(e);
|
|
1491
|
-
}
|
|
1492
|
-
// Edge Case 3: Add warning for missing onClick with confirmOptions
|
|
1493
|
-
if (confirmOptions && !onClick) { // Use destructured confirmOptions and onClick
|
|
1494
|
-
console.warn("ZestButton: 'confirmOptions' are provided but 'onClick' handler is missing. The button will confirm but perform no action.");
|
|
1495
|
-
}
|
|
1789
|
+
const handleConfirmClick = (e) => {
|
|
1496
1790
|
if (awaitingConfirm) {
|
|
1497
|
-
|
|
1498
|
-
|
|
1791
|
+
stopConfirmation();
|
|
1792
|
+
handleClick(e);
|
|
1793
|
+
return;
|
|
1499
1794
|
}
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
setCurrentChildren(`${displayLabel} (${timeoutSecs}s)`); // Initial display
|
|
1504
|
-
confirmIntervalRef.current = setInterval(() => {
|
|
1505
|
-
const elapsed = Date.now() - startTime;
|
|
1506
|
-
const timeRemaining = timeoutSecs - Math.floor(elapsed / 1000);
|
|
1507
|
-
if (timeRemaining <= 0) {
|
|
1508
|
-
stopWaiting();
|
|
1509
|
-
setWasFailed(true); // Indicate failure for shake animation
|
|
1510
|
-
// Bug 1: Clear any existing timeout before setting a new one
|
|
1511
|
-
if (failTimeoutRef.current) {
|
|
1512
|
-
clearTimeout(failTimeoutRef.current);
|
|
1513
|
-
}
|
|
1514
|
-
failTimeoutRef.current = setTimeout(() => {
|
|
1515
|
-
setWasFailed(false);
|
|
1516
|
-
failTimeoutRef.current = null; // Clear ref after timeout fires
|
|
1517
|
-
}, 400); // Shake animation duration
|
|
1518
|
-
}
|
|
1519
|
-
else {
|
|
1520
|
-
setCurrentChildren(`${displayLabel} (${timeRemaining}s)`);
|
|
1795
|
+
if (confirmOptions) {
|
|
1796
|
+
if (!onClick) {
|
|
1797
|
+
console.warn("ZestButton: 'confirmOptions' are provided but 'onClick' handler is missing.");
|
|
1521
1798
|
}
|
|
1522
|
-
|
|
1523
|
-
};
|
|
1524
|
-
// auto-reset success/failure state
|
|
1525
|
-
useEffect(() => {
|
|
1526
|
-
if ((wasSuccessful || wasFailed) && autoResetAfterMs) {
|
|
1527
|
-
const timeout = setTimeout(() => {
|
|
1528
|
-
setWasSuccessful(false);
|
|
1529
|
-
setWasFailed(false);
|
|
1530
|
-
}, autoResetAfterMs);
|
|
1531
|
-
return () => clearTimeout(timeout);
|
|
1799
|
+
startConfirmation();
|
|
1532
1800
|
}
|
|
1533
|
-
|
|
1534
|
-
|
|
1801
|
+
else {
|
|
1802
|
+
handleClick(e);
|
|
1803
|
+
}
|
|
1804
|
+
};
|
|
1535
1805
|
useEffect(() => {
|
|
1536
1806
|
if (!isDefault || isDisabled)
|
|
1537
1807
|
return;
|
|
@@ -1540,54 +1810,48 @@ zest, // New parent prop
|
|
|
1540
1810
|
if (e.key === "Enter" &&
|
|
1541
1811
|
!e.repeat &&
|
|
1542
1812
|
!e.defaultPrevented &&
|
|
1543
|
-
!(target instanceof HTMLTextAreaElement)
|
|
1813
|
+
!(target instanceof HTMLTextAreaElement) &&
|
|
1814
|
+
buttonRef.current &&
|
|
1815
|
+
document.activeElement !== buttonRef.current // Optional: prevent double-action if button is focused
|
|
1816
|
+
) {
|
|
1544
1817
|
e.preventDefault();
|
|
1545
|
-
|
|
1546
|
-
handleConfirmClick(e);
|
|
1818
|
+
buttonRef.current.click();
|
|
1547
1819
|
}
|
|
1548
1820
|
};
|
|
1549
1821
|
document.addEventListener("keydown", listener);
|
|
1550
1822
|
return () => document.removeEventListener("keydown", listener);
|
|
1551
|
-
}, [isDefault, isDisabled
|
|
1552
|
-
// cleanup on unmount
|
|
1553
|
-
useEffect(() => {
|
|
1554
|
-
return () => {
|
|
1555
|
-
if (confirmIntervalRef.current) {
|
|
1556
|
-
clearInterval(confirmIntervalRef.current);
|
|
1557
|
-
}
|
|
1558
|
-
// Bug 1: Add cleanup for failTimeoutRef
|
|
1559
|
-
if (failTimeoutRef.current) {
|
|
1560
|
-
clearTimeout(failTimeoutRef.current);
|
|
1561
|
-
}
|
|
1562
|
-
};
|
|
1563
|
-
}, []);
|
|
1823
|
+
}, [isDefault, isDisabled]);
|
|
1564
1824
|
const renderLeftIcon = () => {
|
|
1565
1825
|
if (effectiveBusy) {
|
|
1566
1826
|
return (jsxRuntimeExports.jsx("span", { className: `${styles.icon} ${styles.fadeIn}`, children: jsxRuntimeExports.jsx(FaSpinner, { className: styles.spinner }) }));
|
|
1567
1827
|
}
|
|
1568
|
-
|
|
1828
|
+
if (wasSuccessful && showCheckmark) {
|
|
1569
1829
|
return (jsxRuntimeExports.jsx("span", { className: `${styles.icon} ${styles.fadeIn}`, children: jsxRuntimeExports.jsx(AnimatedCheckmark, {}) }));
|
|
1570
1830
|
}
|
|
1571
|
-
|
|
1831
|
+
if (wasFailed && showFailIcon) {
|
|
1572
1832
|
return (jsxRuntimeExports.jsx("span", { className: `${styles.icon} ${styles.fadeIn}`, children: jsxRuntimeExports.jsx(AnimatedX, {}) }));
|
|
1573
1833
|
}
|
|
1574
|
-
|
|
1834
|
+
if (iconLeft) {
|
|
1575
1835
|
return jsxRuntimeExports.jsx("span", { className: styles.icon, children: iconLeft });
|
|
1576
1836
|
}
|
|
1577
1837
|
return null;
|
|
1578
1838
|
};
|
|
1579
1839
|
return (jsxRuntimeExports.jsx("button", { ref: buttonRef, className: [
|
|
1580
1840
|
styles.button,
|
|
1581
|
-
styles[buttonStyle],
|
|
1841
|
+
styles[buttonStyle],
|
|
1582
1842
|
styles[variant],
|
|
1583
1843
|
styles[size],
|
|
1584
1844
|
fullWidth ? styles.fullWidth : "",
|
|
1585
1845
|
isDisabled ? styles.disabled : "",
|
|
1586
1846
|
wasFailed ? styles.shake : "",
|
|
1587
|
-
effectiveTheme === 'light' ? styles['force-light'] : styles['force-dark'],
|
|
1847
|
+
effectiveTheme === 'light' ? styles['force-light'] : styles['force-dark'],
|
|
1588
1848
|
className,
|
|
1589
1849
|
].join(" "), disabled: isDisabled, "aria-busy": effectiveBusy, onClick: handleConfirmClick, ...props, children: jsxRuntimeExports.jsxs("span", { className: styles.inner, children: [renderLeftIcon(), jsxRuntimeExports.jsxs("span", { className: styles.content, children: [currentChildren, iconRight && jsxRuntimeExports.jsx("span", { className: styles.icon, children: iconRight })] })] }) }));
|
|
1590
1850
|
};
|
|
1591
1851
|
|
|
1592
|
-
|
|
1852
|
+
const ZestProvider = ({ config, children }) => {
|
|
1853
|
+
return (jsxRuntimeExports.jsx(ZestContext.Provider, { value: config, children: children }));
|
|
1854
|
+
};
|
|
1855
|
+
|
|
1856
|
+
export { ZestButton, ZestProvider, useZest };
|
|
1593
1857
|
//# sourceMappingURL=index.esm.js.map
|