jattac.libs.web.zest-button 1.2.2 → 1.2.4

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.esm.js CHANGED
@@ -1,5 +1,6 @@
1
- import require$$0, { useEffect, useState, useRef } from 'react';
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
- // --- Components ---
1391
- 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" }) }));
1392
- 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" })] }));
1393
- const ZestButton = ({ className = "", disabled, children, onClick, // Destructure onClick here
1394
- zest, // New parent prop
1395
- ...props }) => {
1396
- // Destructure custom props from 'zest' with defaults
1397
- const { visualOptions = {}, busyOptions = {}, successOptions = {}, confirmOptions, // confirmOptions can be undefined
1398
- isDefault = false, theme = 'system', buttonStyle = 'solid', } = zest || {}; // Provide empty object as default for zest
1399
- const { variant = "standard", size = "md", fullWidth = false, iconLeft, iconRight, } = visualOptions;
1400
- const { handleInternally = true, preventRageClick = true, minBusyDurationMs = 500, } = busyOptions;
1401
- const { showCheckmark = true, showFailIcon = true, autoResetAfterMs = 2000, } = successOptions;
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
- const [internalBusy, setInternalBusy] = useState(false);
1414
- const [wasSuccessful, setWasSuccessful] = useState(false);
1415
- const [wasFailed, setWasFailed] = useState(false);
1416
- const buttonRef = useRef(null);
1417
- const failTimeoutRef = useRef(null); // Bug 1: Add useRef for failTimeout
1418
- const [currentChildren, setCurrentChildren] = useState(children);
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
- // interval ref for confirm countdown
1678
+ const [currentChildren, setCurrentChildren] = useState(originalChildren);
1421
1679
  const confirmIntervalRef = useRef(null);
1422
- // Theme state and detection
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
- setWasSuccessful(false);
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
- if (showCheckmark)
1473
- setWasSuccessful(true);
1778
+ endBusy(true);
1474
1779
  }
1475
1780
  catch (err) {
1476
1781
  console.error(err);
1477
- if (showFailIcon)
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 = async (e) => {
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
- stopWaiting();
1498
- return handleClick(e);
1791
+ stopConfirmation();
1792
+ handleClick(e);
1793
+ return;
1499
1794
  }
1500
- const { displayLabel, timeoutSecs } = confirmOptions; // Use destructured confirmOptions
1501
- const startTime = Date.now();
1502
- setAwaitingConfirm(true);
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
- }, 1000); // Update once per second
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
- }, [wasSuccessful, wasFailed, autoResetAfterMs]);
1534
- // Enter key handler if isDefault
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
- // Bug 2: Directly call handleConfirmClick to respect confirmation logic
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, handleConfirmClick]); // Bug 2: Add handleConfirmClick to dependencies
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
- else if (wasSuccessful && showCheckmark) {
1828
+ if (wasSuccessful && showCheckmark) {
1569
1829
  return (jsxRuntimeExports.jsx("span", { className: `${styles.icon} ${styles.fadeIn}`, children: jsxRuntimeExports.jsx(AnimatedCheckmark, {}) }));
1570
1830
  }
1571
- else if (wasFailed && showFailIcon) {
1831
+ if (wasFailed && showFailIcon) {
1572
1832
  return (jsxRuntimeExports.jsx("span", { className: `${styles.icon} ${styles.fadeIn}`, children: jsxRuntimeExports.jsx(AnimatedX, {}) }));
1573
1833
  }
1574
- else if (iconLeft) {
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], // Apply buttonStyle class
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'], // Apply theme override class
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
- export { ZestButton as default };
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