jattac.libs.web.zest-button 1.3.0 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +10 -9
- package/dist/ZestButton.d.ts +23 -2
- package/dist/ZestDropdownMenu.d.ts +20 -0
- package/dist/ZestDropdownMenuItem.d.ts +9 -0
- package/dist/index.cjs.js +192 -7
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +22 -1
- package/dist/index.esm.js +175 -9
- package/dist/index.esm.js.map +1 -1
- package/{docs → documentation}/api.md +25 -0
- package/{docs → documentation}/examples.md +62 -0
- package/{docs → documentation}/features.md +24 -0
- package/package.json +79 -69
- package/dist/ZestContext.d.ts +0 -9
- package/dist/ZestProvider.d.ts +0 -8
- package/dist/semanticTypeDefaults.d.ts +0 -4
- /package/{docs → documentation}/breaking-changes.md +0 -0
- /package/{docs → documentation}/configuration.md +0 -0
- /package/{docs → documentation}/development.md +0 -0
package/dist/index.d.ts
CHANGED
|
@@ -37,6 +37,21 @@ interface ConfirmOptions {
|
|
|
37
37
|
displayLabel: string;
|
|
38
38
|
timeoutSecs: number;
|
|
39
39
|
}
|
|
40
|
+
/**
|
|
41
|
+
* A single secondary action in a ZestButton's dropdown menu. Runs through the
|
|
42
|
+
* same busy/confirm machinery as the main button, independently per item.
|
|
43
|
+
*/
|
|
44
|
+
interface ZestDropdownOption {
|
|
45
|
+
key?: string;
|
|
46
|
+
label: React.ReactNode;
|
|
47
|
+
icon?: React.ReactNode;
|
|
48
|
+
disabled?: boolean;
|
|
49
|
+
semanticType?: SemanticType;
|
|
50
|
+
onClick?: (e: Event) => void | Promise<void>;
|
|
51
|
+
busyOptions?: BusyOptions;
|
|
52
|
+
successOptions?: SuccessOptions;
|
|
53
|
+
confirmOptions?: ConfirmOptions;
|
|
54
|
+
}
|
|
40
55
|
interface ZestCustomProps {
|
|
41
56
|
visualOptions?: VisualOptions;
|
|
42
57
|
busyOptions?: BusyOptions;
|
|
@@ -46,6 +61,12 @@ interface ZestCustomProps {
|
|
|
46
61
|
theme?: ZestTheme;
|
|
47
62
|
buttonStyle?: ZestButtonStyle;
|
|
48
63
|
semanticType?: SemanticType;
|
|
64
|
+
dropdownOptions?: ZestDropdownOption[];
|
|
65
|
+
dropdownAriaLabel?: string;
|
|
66
|
+
/** Theme for the dropdown menu panel; inherits the button's own `theme` when unset. */
|
|
67
|
+
dropdownTheme?: ZestTheme;
|
|
68
|
+
/** Minimum width for the dropdown menu panel (px number or CSS length string); defaults to the split control's own rendered width. */
|
|
69
|
+
dropdownWidth?: number | string;
|
|
49
70
|
}
|
|
50
71
|
/**
|
|
51
72
|
* All supported props
|
|
@@ -68,4 +89,4 @@ interface ZestButtonConfigProviderProps {
|
|
|
68
89
|
declare const ZestButtonConfigProvider: React.FC<ZestButtonConfigProviderProps>;
|
|
69
90
|
|
|
70
91
|
export { ZestButtonConfigProvider, ZestButton as default, useZestButtonConfig };
|
|
71
|
-
export type { ConfirmOptions, CustomZestSemanticTypes, SemanticType, ZestButtonProps, ZestButtonStyle, ZestCustomProps, ZestGlobalConfig, ZestSize, ZestTheme, ZestVariant };
|
|
92
|
+
export type { BusyOptions, ConfirmOptions, CustomZestSemanticTypes, SemanticType, SuccessOptions, ZestButtonProps, ZestButtonStyle, ZestCustomProps, ZestDropdownOption, ZestGlobalConfig, ZestSize, ZestTheme, ZestVariant };
|
package/dist/index.esm.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import require$$0, { useContext, useState, useRef, useEffect, useCallback } from 'react';
|
|
2
|
-
import { FaSpinner } from 'react-icons/fa6';
|
|
1
|
+
import require$$0, { useContext, useState, useRef, useEffect, useCallback, useLayoutEffect } from 'react';
|
|
2
|
+
import { FaSpinner, FaChevronDown } from 'react-icons/fa6';
|
|
3
3
|
import { FaShareAlt, FaPrint, FaSyncAlt, FaUpload, FaDownload, FaArrowRight, FaTimes, FaTrash, FaEdit, FaSave, FaPlus } from 'react-icons/fa';
|
|
4
|
+
import * as DropdownMenu from '@radix-ui/react-dropdown-menu';
|
|
4
5
|
|
|
5
6
|
var jsxRuntime = {exports: {}};
|
|
6
7
|
|
|
@@ -1384,8 +1385,8 @@ function styleInject(css, ref) {
|
|
|
1384
1385
|
}
|
|
1385
1386
|
}
|
|
1386
1387
|
|
|
1387
|
-
var css_248z = "/* Styles/ZestButton.module.css */\n\n/* Define base variables for light mode */\n:root {\n --zest-color-standard-bg: #1e40af;\n --zest-color-standard-bg-hover: #1a35a0;\n --zest-color-standard-text: white;\n --zest-color-standard-border: #1e40af;\n\n --zest-color-success-bg: #059669;\n --zest-color-success-bg-hover: #047857;\n --zest-color-success-text: white;\n --zest-color-success-border: #059669;\n\n --zest-color-danger-bg: #dc2626;\n --zest-color-danger-bg-hover: #b91c1c;\n --zest-color-danger-text: white;\n --zest-color-danger-border: #dc2626;\n\n --zest-color-disabled-bg: #cbd5e1;\n --zest-color-disabled-bg-hover: #aeb9c4;\n --zest-color-disabled-text: #6b7280;\n --zest-color-disabled-border: #cbd5e1;\n\n --zest-color-icon-success: #10b981;\n --zest-color-icon-fail: #ef4444;\n}\n\n/* Dark mode adjustments via system preference */\n@media (prefers-color-scheme: dark) {\n :root {\n --zest-color-standard-bg: #3b82f6;\n --zest-color-standard-bg-hover: #2563eb;\n --zest-color-standard-text: white;\n --zest-color-standard-border: #3b82f6;\n\n --zest-color-success-bg: #34d399;\n --zest-color-success-bg-hover: #10b981;\n --zest-color-success-text: white;\n --zest-color-success-border: #34d399;\n\n --zest-color-danger-bg: #ef4444;\n --zest-color-danger-bg-hover: #dc2626;\n --zest-color-danger-text: white;\n --zest-color-danger-border: #ef4444;\n\n --zest-color-disabled-bg: #4b5563;\n --zest-color-disabled-bg-hover: #6b7280;\n --zest-color-disabled-text: #9ca3af;\n --zest-color-disabled-border: #4b5563;\n\n --zest-color-icon-success: #34d399;\n --zest-color-icon-fail: #ef4444;\n }\n}\n\n/* Theme override classes */\n.ZestButton-module_force-light__zZTIZ {\n --zest-color-standard-bg: #1e40af;\n --zest-color-standard-bg-hover: #1a35a0;\n --zest-color-standard-text: white;\n --zest-color-standard-border: #1e40af;\n\n --zest-color-success-bg: #059669;\n --zest-color-success-bg-hover: #047857;\n --zest-color-success-text: white;\n --zest-color-success-border: #059669;\n\n --zest-color-danger-bg: #dc2626;\n --zest-color-danger-bg-hover: #b91c1c;\n --zest-color-danger-text: white;\n --zest-color-danger-border: #dc2626;\n\n --zest-color-disabled-bg: #cbd5e1;\n --zest-color-disabled-bg-hover: #aeb9c4;\n --zest-color-disabled-text: #6b7280;\n --zest-color-disabled-border: #cbd5e1;\n\n --zest-color-icon-success: #10b981;\n --zest-color-icon-fail: #ef4444;\n}\n\n.ZestButton-module_force-dark__cx74D {\n --zest-color-standard-bg: #3b82f6;\n --zest-color-standard-bg-hover: #2563eb;\n --zest-color-standard-text: white;\n --zest-color-standard-border: #3b82f6;\n\n --zest-color-success-bg: #34d399;\n --zest-color-success-bg-hover: #10b981;\n --zest-color-success-text: white;\n --zest-color-success-border: #34d399;\n\n --zest-color-danger-bg: #ef4444;\n --zest-color-danger-bg-hover: #dc2626;\n --zest-color-danger-text: white;\n --zest-color-danger-border: #ef4444;\n\n --zest-color-disabled-bg: #4b5563;\n --zest-color-disabled-bg-hover: #6b7280;\n --zest-color-disabled-text: #9ca3af;\n --zest-color-disabled-border: #4b5563;\n\n --zest-color-icon-success: #34d399;\n --zest-color-icon-fail: #ef4444;\n}\n\n\n.ZestButton-module_button__KDafc {\n font-family: \"Segoe UI\", Roboto, sans-serif;\n font-weight: 600;\n border-radius: 8px;\n cursor: pointer;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n transition: background-color 0.2s ease, box-shadow 0.2s ease, color 0.2s ease, border-color 0.2s ease;\n position: relative;\n padding: 0.6em 1.2em;\n overflow: hidden;\n border: none; /* Default to no border, overridden by outline/dashed */\n}\n\n/* Base styles for different button styles */\n.ZestButton-module_solid__cu4tr {\n background-color: var(--zest-color-standard-bg);\n color: var(--zest-color-standard-text);\n}\n.ZestButton-module_solid__cu4tr:hover:not(:disabled) {\n background-color: var(--zest-color-standard-bg-hover);\n}\n\n.ZestButton-module_outline__esgLq {\n background-color: transparent;\n border: 2px solid var(--zest-color-standard-border);\n color: var(--zest-color-standard-border);\n}\n.ZestButton-module_outline__esgLq:hover:not(:disabled) {\n background-color: var(--zest-color-standard-bg);\n color: var(--zest-color-standard-text);\n}\n\n.ZestButton-module_text__8X1xD {\n background-color: transparent;\n border: none;\n color: var(--zest-color-standard-bg);\n}\n.ZestButton-module_text__8X1xD:hover:not(:disabled) {\n background-color: rgba(var(--zest-color-standard-bg-rgb, 30, 64, 175), 0.1); /* Fallback RGB for text hover */\n}\n\n.ZestButton-module_dashed__ebKYF {\n background-color: transparent;\n border: 2px dashed var(--zest-color-standard-border);\n color: var(--zest-color-standard-border);\n}\n.ZestButton-module_dashed__ebKYF:hover:not(:disabled) {\n background-color: rgba(var(--zest-color-standard-bg-rgb, 30, 64, 175), 0.1); /* Fallback RGB for dashed hover */\n}\n\n\n/* Variant overrides for solid style */\n.ZestButton-module_solid__cu4tr.ZestButton-module_standard__T3EGM { /* Already handled by .solid base */ }\n.ZestButton-module_solid__cu4tr.ZestButton-module_success__XEptA {\n background-color: var(--zest-color-success-bg);\n color: var(--zest-color-success-text);\n}\n.ZestButton-module_solid__cu4tr.ZestButton-module_success__XEptA:hover:not(:disabled) {\n background-color: var(--zest-color-success-bg-hover);\n}\n.ZestButton-module_solid__cu4tr.ZestButton-module_danger__nJpJ- {\n background-color: var(--zest-color-danger-bg);\n color: var(--zest-color-danger-text);\n}\n.ZestButton-module_solid__cu4tr.ZestButton-module_danger__nJpJ-:hover:not(:disabled) {\n background-color: var(--zest-color-danger-bg-hover);\n}\n\n/* Variant overrides for outline style */\n.ZestButton-module_outline__esgLq.ZestButton-module_standard__T3EGM { /* Already handled by .outline base */ }\n.ZestButton-module_outline__esgLq.ZestButton-module_success__XEptA {\n border-color: var(--zest-color-success-border);\n color: var(--zest-color-success-border);\n}\n.ZestButton-module_outline__esgLq.ZestButton-module_success__XEptA:hover:not(:disabled) {\n background-color: var(--zest-color-success-bg);\n color: var(--zest-color-success-text);\n}\n.ZestButton-module_outline__esgLq.ZestButton-module_danger__nJpJ- {\n border-color: var(--zest-color-danger-border);\n color: var(--zest-color-danger-border);\n}\n.ZestButton-module_outline__esgLq.ZestButton-module_danger__nJpJ-:hover:not(:disabled) {\n background-color: var(--zest-color-danger-bg);\n color: var(--zest-color-danger-text);\n}\n\n/* Variant overrides for text style */\n.ZestButton-module_text__8X1xD.ZestButton-module_standard__T3EGM { /* Already handled by .text base */ }\n.ZestButton-module_text__8X1xD.ZestButton-module_success__XEptA {\n color: var(--zest-color-success-bg);\n}\n.ZestButton-module_text__8X1xD.ZestButton-module_success__XEptA:hover:not(:disabled) {\n background-color: rgba(var(--zest-color-success-bg-rgb, 5, 150, 105), 0.1);\n}\n.ZestButton-module_text__8X1xD.ZestButton-module_danger__nJpJ- {\n color: var(--zest-color-danger-bg);\n}\n.ZestButton-module_text__8X1xD.ZestButton-module_danger__nJpJ-:hover:not(:disabled) {\n background-color: rgba(var(--zest-color-danger-bg-rgb, 220, 38, 38), 0.1);\n}\n\n/* Variant overrides for dashed style */\n.ZestButton-module_dashed__ebKYF.ZestButton-module_standard__T3EGM { /* Already handled by .dashed base */ }\n.ZestButton-module_dashed__ebKYF.ZestButton-module_success__XEptA {\n border-color: var(--zest-color-success-border);\n color: var(--zest-color-success-border);\n}\n.ZestButton-module_dashed__ebKYF.ZestButton-module_success__XEptA:hover:not(:disabled) {\n background-color: rgba(var(--zest-color-success-bg-rgb, 5, 150, 105), 0.1);\n}\n.ZestButton-module_dashed__ebKYF.ZestButton-module_danger__nJpJ- {\n border-color: var(--zest-color-danger-border);\n color: var(--zest-color-danger-border);\n}\n.ZestButton-module_dashed__ebKYF.ZestButton-module_danger__nJpJ-:hover:not(:disabled) {\n background-color: rgba(var(--zest-color-danger-bg-rgb, 220, 38, 38), 0.1);\n}\n\n\n/* Disabled state overrides for different button styles */\n.ZestButton-module_solid__cu4tr:disabled, .ZestButton-module_solid__cu4tr.ZestButton-module_disabled__gw6y3 {\n background-color: var(--zest-color-disabled-bg);\n color: var(--zest-color-disabled-text);\n border-color: var(--zest-color-disabled-border);\n}\n.ZestButton-module_outline__esgLq:disabled, .ZestButton-module_outline__esgLq.ZestButton-module_disabled__gw6y3 {\n background-color: transparent;\n border-color: var(--zest-color-disabled-border);\n color: var(--zest-color-disabled-text);\n}\n.ZestButton-module_text__8X1xD:disabled, .ZestButton-module_text__8X1xD.ZestButton-module_disabled__gw6y3 {\n background-color: transparent;\n color: var(--zest-color-disabled-text);\n}\n.ZestButton-module_dashed__ebKYF:disabled, .ZestButton-module_dashed__ebKYF.ZestButton-module_disabled__gw6y3 {\n background-color: transparent;\n border-color: var(--zest-color-disabled-border);\n color: var(--zest-color-disabled-text);\n}\n\n\n/* Layout */\n.ZestButton-module_stretch__CTLNw {\n width: 100%;\n}\n\n.ZestButton-module_sm__G1vAP {\n font-size: 0.85rem;\n padding: 0.4em 0.8em;\n}\n\n.ZestButton-module_md__Y-PMO {\n font-size: 1rem;\n padding: 0.6em 1.2em;\n}\n\n.ZestButton-module_lg__AQgdf {\n font-size: 1.125rem;\n padding: 0.8em 1.6em;\n}\n\n.ZestButton-module_inner__1j2Fr {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n position: relative;\n}\n\n.ZestButton-module_spinner__l2hLe {\n position: absolute;\n width: 1em;\n height: 1em;\n animation: ZestButton-module_spin__4asdw 0.75s linear infinite;\n color: currentColor; /* Inherit color from button text */\n}\n\n.ZestButton-module_content__hlea3 {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 0.5em;\n}\n\n.ZestButton-module_label__8x263 {\n display: inline-block;\n}\n\n.ZestButton-module_icon__B3DFi {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n}\n\n/* Success + Fail Icons */\n.ZestButton-module_animatedCheck__8K4K- {\n width: 1.25em;\n height: 1.25em;\n color: var(--zest-color-icon-success);\n animation: ZestButton-module_fadeIn__iEave 0.25s ease-out;\n}\n\n.ZestButton-module_animatedX__KQnt7 {\n width: 1.25em;\n height: 1.25em;\n color: var(--zest-color-icon-fail);\n animation: ZestButton-module_fadeIn__iEave 0.25s ease-out;\n}\n\n/* Stroke animations */\n.ZestButton-module_animatedCheck__8K4K- path {\n stroke-dasharray: 24;\n stroke-dashoffset: 24;\n animation: ZestButton-module_drawCheck__3DyjT 0.4s ease-out forwards;\n}\n\n.ZestButton-module_animatedX__KQnt7 line {\n stroke-dasharray: 24;\n stroke-dashoffset: 24;\n animation: ZestButton-module_drawCheck__3DyjT 0.4s ease-out forwards;\n}\n\n/* New: apply spacing between left icon and text */\n.ZestButton-module_icon__B3DFi:first-child {\n margin-right: 0.7em;\n}\n\n/* Optional override for iconRight if needed */\n.ZestButton-module_icon__B3DFi:last-child {\n margin-left: 0.7em;\n}\n\n/* Animations */\n@keyframes ZestButton-module_spin__4asdw {\n to {\n transform: rotate(360deg);\n }\n}\n\n@keyframes ZestButton-module_drawCheck__3DyjT {\n to {\n stroke-dashoffset: 0;\n }\n}\n\n@keyframes ZestButton-module_fadeIn__iEave {\n from {\n opacity: 0;\n transform: scale(0.8);\n }\n to {\n opacity: 1;\n transform: scale(1);\n }\n}\n\n@keyframes ZestButton-module_shakeIt__ox-R3 {\n 0%,\n 100% {\n transform: translateX(0);\n }\n 20%,\n 60% {\n transform: translateX(-4px);\n }\n 40%,\n 80% {\n transform: translateX(4px);\n }\n}\n\n.ZestButton-module_shake__NtIjf {\n animation: ZestButton-module_shakeIt__ox-R3 0.4s ease;\n box-shadow: 0 0 0 2px rgba(239, 68, 68, 0.6);\n}\n\n/* Mobile-first adjustments for hit area */\n@media (max-width: 768px) {\n .ZestButton-module_button__KDafc {\n min-height: 44px; /* Ensure a minimum touch target size for accessibility */\n }\n\n .ZestButton-module_sm__G1vAP {\n padding: 0.6em 1em; /* Increase padding for small buttons on mobile */\n }\n\n .ZestButton-module_md__Y-PMO {\n padding: 0.8em 1.4em; /* Slightly increase padding for medium buttons on mobile */\n }\n}\n\n";
|
|
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","stretch":"ZestButton-module_stretch__CTLNw","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
|
+
var css_248z = "/* Styles/ZestButton.module.css */\r\n\r\n/* Define base variables for light mode */\r\n:root {\r\n --zest-color-standard-bg: #1e40af;\r\n --zest-color-standard-bg-hover: #1a35a0;\r\n --zest-color-standard-text: white;\r\n --zest-color-standard-border: #1e40af;\r\n\r\n --zest-color-success-bg: #059669;\r\n --zest-color-success-bg-hover: #047857;\r\n --zest-color-success-text: white;\r\n --zest-color-success-border: #059669;\r\n\r\n --zest-color-danger-bg: #dc2626;\r\n --zest-color-danger-bg-hover: #b91c1c;\r\n --zest-color-danger-text: white;\r\n --zest-color-danger-border: #dc2626;\r\n\r\n --zest-color-disabled-bg: #cbd5e1;\r\n --zest-color-disabled-bg-hover: #aeb9c4;\r\n --zest-color-disabled-text: #6b7280;\r\n --zest-color-disabled-border: #cbd5e1;\r\n\r\n --zest-color-icon-success: #10b981;\r\n --zest-color-icon-fail: #ef4444;\r\n}\r\n\r\n/* Shape/elevation/motion tokens (additive; no existing --zest-color-* renamed) */\r\n:root {\r\n --zest-radius-sm: 6px; /* menu items */\r\n --zest-radius-md: 8px; /* buttons, menu panel */\r\n --zest-shadow-resting: 0 1px 2px rgba(0, 0, 0, 0.08), 0 1px 1px rgba(0, 0, 0, 0.04);\r\n --zest-shadow-hover: 0 4px 10px rgba(0, 0, 0, 0.12), 0 2px 4px rgba(0, 0, 0, 0.06);\r\n --zest-shadow-menu: 0 8px 24px rgba(0, 0, 0, 0.16);\r\n --zest-focus-ring-color: #2563eb;\r\n --zest-focus-ring: 0 0 0 3px rgba(37, 99, 235, 0.45);\r\n --zest-ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1);\r\n --zest-ease-standard: cubic-bezier(0.2, 0, 0, 1);\r\n --zest-motion-fast: 0.15s;\r\n --zest-motion-base: 0.2s;\r\n}\r\n\r\n/* Dark mode adjustments via system preference */\r\n@media (prefers-color-scheme: dark) {\r\n :root {\r\n --zest-color-standard-bg: #3b82f6;\r\n --zest-color-standard-bg-hover: #2563eb;\r\n --zest-color-standard-text: white;\r\n --zest-color-standard-border: #3b82f6;\r\n\r\n --zest-color-success-bg: #34d399;\r\n --zest-color-success-bg-hover: #10b981;\r\n --zest-color-success-text: white;\r\n --zest-color-success-border: #34d399;\r\n\r\n --zest-color-danger-bg: #ef4444;\r\n --zest-color-danger-bg-hover: #dc2626;\r\n --zest-color-danger-text: white;\r\n --zest-color-danger-border: #ef4444;\r\n\r\n --zest-color-disabled-bg: #4b5563;\r\n --zest-color-disabled-bg-hover: #6b7280;\r\n --zest-color-disabled-text: #9ca3af;\r\n --zest-color-disabled-border: #4b5563;\r\n\r\n --zest-color-icon-success: #34d399;\r\n --zest-color-icon-fail: #ef4444;\r\n\r\n --zest-shadow-resting: 0 1px 2px rgba(0, 0, 0, 0.3), 0 1px 1px rgba(0, 0, 0, 0.2);\r\n --zest-shadow-hover: 0 4px 12px rgba(0, 0, 0, 0.4), 0 2px 6px rgba(0, 0, 0, 0.25);\r\n --zest-focus-ring-color: #60a5fa;\r\n --zest-focus-ring: 0 0 0 3px rgba(96, 165, 250, 0.5);\r\n }\r\n}\r\n\r\n/* Theme override classes */\r\n.ZestButton-module_force-light__zZTIZ {\r\n --zest-color-standard-bg: #1e40af;\r\n --zest-color-standard-bg-hover: #1a35a0;\r\n --zest-color-standard-text: white;\r\n --zest-color-standard-border: #1e40af;\r\n\r\n --zest-color-success-bg: #059669;\r\n --zest-color-success-bg-hover: #047857;\r\n --zest-color-success-text: white;\r\n --zest-color-success-border: #059669;\r\n\r\n --zest-color-danger-bg: #dc2626;\r\n --zest-color-danger-bg-hover: #b91c1c;\r\n --zest-color-danger-text: white;\r\n --zest-color-danger-border: #dc2626;\r\n\r\n --zest-color-disabled-bg: #cbd5e1;\r\n --zest-color-disabled-bg-hover: #aeb9c4;\r\n --zest-color-disabled-text: #6b7280;\r\n --zest-color-disabled-border: #cbd5e1;\r\n\r\n --zest-color-icon-success: #10b981;\r\n --zest-color-icon-fail: #ef4444;\r\n}\r\n\r\n.ZestButton-module_force-dark__cx74D {\r\n --zest-color-standard-bg: #3b82f6;\r\n --zest-color-standard-bg-hover: #2563eb;\r\n --zest-color-standard-text: white;\r\n --zest-color-standard-border: #3b82f6;\r\n\r\n --zest-color-success-bg: #34d399;\r\n --zest-color-success-bg-hover: #10b981;\r\n --zest-color-success-text: white;\r\n --zest-color-success-border: #34d399;\r\n\r\n --zest-color-danger-bg: #ef4444;\r\n --zest-color-danger-bg-hover: #dc2626;\r\n --zest-color-danger-text: white;\r\n --zest-color-danger-border: #ef4444;\r\n\r\n --zest-color-disabled-bg: #4b5563;\r\n --zest-color-disabled-bg-hover: #6b7280;\r\n --zest-color-disabled-text: #9ca3af;\r\n --zest-color-disabled-border: #4b5563;\r\n\r\n --zest-color-icon-success: #34d399;\r\n --zest-color-icon-fail: #ef4444;\r\n\r\n --zest-shadow-resting: 0 1px 2px rgba(0, 0, 0, 0.3), 0 1px 1px rgba(0, 0, 0, 0.2);\r\n --zest-shadow-hover: 0 4px 12px rgba(0, 0, 0, 0.4), 0 2px 6px rgba(0, 0, 0, 0.25);\r\n --zest-focus-ring-color: #60a5fa;\r\n --zest-focus-ring: 0 0 0 3px rgba(96, 165, 250, 0.5);\r\n}\r\n\r\n\r\n.ZestButton-module_button__KDafc {\r\n font-family: \"Segoe UI\", Roboto, sans-serif;\r\n font-weight: 600;\r\n border-radius: var(--zest-radius-md);\r\n cursor: pointer;\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n appearance: none;\r\n -webkit-appearance: none;\r\n -moz-appearance: none;\r\n transition:\r\n background-color var(--zest-motion-base) ease,\r\n box-shadow var(--zest-motion-base) ease,\r\n color var(--zest-motion-base) ease,\r\n border-color var(--zest-motion-base) ease,\r\n transform var(--zest-motion-fast) var(--zest-ease-standard);\r\n position: relative;\r\n padding: 0.6em 1.2em;\r\n overflow: hidden;\r\n border: none; /* Default to no border, overridden by outline/dashed */\r\n box-shadow: var(--zest-shadow-resting);\r\n}\r\n\r\n.ZestButton-module_button__KDafc::-moz-focus-inner {\r\n border: 0;\r\n padding: 0;\r\n}\r\n\r\n.ZestButton-module_button__KDafc:hover:not(:disabled) {\r\n box-shadow: var(--zest-shadow-hover);\r\n transform: translateY(-1px);\r\n}\r\n\r\n.ZestButton-module_button__KDafc:active:not(:disabled) {\r\n transform: translateY(0) scale(0.97);\r\n transition-duration: 0.08s;\r\n}\r\n\r\n.ZestButton-module_button__KDafc:disabled {\r\n box-shadow: none;\r\n cursor: not-allowed;\r\n transform: none;\r\n}\r\n\r\n.ZestButton-module_button__KDafc:focus-visible {\r\n outline: none;\r\n box-shadow: var(--zest-shadow-resting), var(--zest-focus-ring);\r\n}\r\n\r\n/* Base styles for different button styles */\r\n.ZestButton-module_solid__cu4tr {\r\n background-color: var(--zest-color-standard-bg);\r\n color: var(--zest-color-standard-text);\r\n}\r\n.ZestButton-module_solid__cu4tr:hover:not(:disabled) {\r\n background-color: var(--zest-color-standard-bg-hover);\r\n}\r\n\r\n.ZestButton-module_outline__esgLq {\r\n background-color: transparent;\r\n border: 2px solid var(--zest-color-standard-border);\r\n color: var(--zest-color-standard-border);\r\n}\r\n.ZestButton-module_outline__esgLq:hover:not(:disabled) {\r\n background-color: var(--zest-color-standard-bg);\r\n color: var(--zest-color-standard-text);\r\n}\r\n\r\n.ZestButton-module_text__8X1xD {\r\n background-color: transparent;\r\n border: none;\r\n color: var(--zest-color-standard-bg);\r\n box-shadow: none; /* transparent background has nothing for a shadow to sit under */\r\n}\r\n.ZestButton-module_text__8X1xD:hover:not(:disabled) {\r\n background-color: rgba(var(--zest-color-standard-bg-rgb, 30, 64, 175), 0.1); /* Fallback RGB for text hover */\r\n box-shadow: none;\r\n}\r\n\r\n.ZestButton-module_dashed__ebKYF {\r\n background-color: transparent;\r\n border: 2px dashed var(--zest-color-standard-border);\r\n color: var(--zest-color-standard-border);\r\n}\r\n.ZestButton-module_dashed__ebKYF:hover:not(:disabled) {\r\n background-color: rgba(var(--zest-color-standard-bg-rgb, 30, 64, 175), 0.1); /* Fallback RGB for dashed hover */\r\n}\r\n\r\n\r\n/* Variant overrides for solid style */\r\n.ZestButton-module_solid__cu4tr.ZestButton-module_standard__T3EGM { /* Already handled by .solid base */ }\r\n.ZestButton-module_solid__cu4tr.ZestButton-module_success__XEptA {\r\n background-color: var(--zest-color-success-bg);\r\n color: var(--zest-color-success-text);\r\n}\r\n.ZestButton-module_solid__cu4tr.ZestButton-module_success__XEptA:hover:not(:disabled) {\r\n background-color: var(--zest-color-success-bg-hover);\r\n}\r\n.ZestButton-module_solid__cu4tr.ZestButton-module_danger__nJpJ- {\r\n background-color: var(--zest-color-danger-bg);\r\n color: var(--zest-color-danger-text);\r\n}\r\n.ZestButton-module_solid__cu4tr.ZestButton-module_danger__nJpJ-:hover:not(:disabled) {\r\n background-color: var(--zest-color-danger-bg-hover);\r\n}\r\n\r\n/* Variant overrides for outline style */\r\n.ZestButton-module_outline__esgLq.ZestButton-module_standard__T3EGM { /* Already handled by .outline base */ }\r\n.ZestButton-module_outline__esgLq.ZestButton-module_success__XEptA {\r\n border-color: var(--zest-color-success-border);\r\n color: var(--zest-color-success-border);\r\n}\r\n.ZestButton-module_outline__esgLq.ZestButton-module_success__XEptA:hover:not(:disabled) {\r\n background-color: var(--zest-color-success-bg);\r\n color: var(--zest-color-success-text);\r\n}\r\n.ZestButton-module_outline__esgLq.ZestButton-module_danger__nJpJ- {\r\n border-color: var(--zest-color-danger-border);\r\n color: var(--zest-color-danger-border);\r\n}\r\n.ZestButton-module_outline__esgLq.ZestButton-module_danger__nJpJ-:hover:not(:disabled) {\r\n background-color: var(--zest-color-danger-bg);\r\n color: var(--zest-color-danger-text);\r\n}\r\n\r\n/* Variant overrides for text style */\r\n.ZestButton-module_text__8X1xD.ZestButton-module_standard__T3EGM { /* Already handled by .text base */ }\r\n.ZestButton-module_text__8X1xD.ZestButton-module_success__XEptA {\r\n color: var(--zest-color-success-bg);\r\n}\r\n.ZestButton-module_text__8X1xD.ZestButton-module_success__XEptA:hover:not(:disabled) {\r\n background-color: rgba(var(--zest-color-success-bg-rgb, 5, 150, 105), 0.1);\r\n}\r\n.ZestButton-module_text__8X1xD.ZestButton-module_danger__nJpJ- {\r\n color: var(--zest-color-danger-bg);\r\n}\r\n.ZestButton-module_text__8X1xD.ZestButton-module_danger__nJpJ-:hover:not(:disabled) {\r\n background-color: rgba(var(--zest-color-danger-bg-rgb, 220, 38, 38), 0.1);\r\n}\r\n\r\n/* Variant overrides for dashed style */\r\n.ZestButton-module_dashed__ebKYF.ZestButton-module_standard__T3EGM { /* Already handled by .dashed base */ }\r\n.ZestButton-module_dashed__ebKYF.ZestButton-module_success__XEptA {\r\n border-color: var(--zest-color-success-border);\r\n color: var(--zest-color-success-border);\r\n}\r\n.ZestButton-module_dashed__ebKYF.ZestButton-module_success__XEptA:hover:not(:disabled) {\r\n background-color: rgba(var(--zest-color-success-bg-rgb, 5, 150, 105), 0.1);\r\n}\r\n.ZestButton-module_dashed__ebKYF.ZestButton-module_danger__nJpJ- {\r\n border-color: var(--zest-color-danger-border);\r\n color: var(--zest-color-danger-border);\r\n}\r\n.ZestButton-module_dashed__ebKYF.ZestButton-module_danger__nJpJ-:hover:not(:disabled) {\r\n background-color: rgba(var(--zest-color-danger-bg-rgb, 220, 38, 38), 0.1);\r\n}\r\n\r\n\r\n/* Disabled state overrides for different button styles */\r\n.ZestButton-module_solid__cu4tr:disabled, .ZestButton-module_solid__cu4tr.ZestButton-module_disabled__gw6y3 {\r\n background-color: var(--zest-color-disabled-bg);\r\n color: var(--zest-color-disabled-text);\r\n border-color: var(--zest-color-disabled-border);\r\n}\r\n.ZestButton-module_outline__esgLq:disabled, .ZestButton-module_outline__esgLq.ZestButton-module_disabled__gw6y3 {\r\n background-color: transparent;\r\n border-color: var(--zest-color-disabled-border);\r\n color: var(--zest-color-disabled-text);\r\n}\r\n.ZestButton-module_text__8X1xD:disabled, .ZestButton-module_text__8X1xD.ZestButton-module_disabled__gw6y3 {\r\n background-color: transparent;\r\n color: var(--zest-color-disabled-text);\r\n}\r\n.ZestButton-module_dashed__ebKYF:disabled, .ZestButton-module_dashed__ebKYF.ZestButton-module_disabled__gw6y3 {\r\n background-color: transparent;\r\n border-color: var(--zest-color-disabled-border);\r\n color: var(--zest-color-disabled-text);\r\n}\r\n\r\n\r\n/* Layout */\r\n.ZestButton-module_stretch__CTLNw {\r\n width: 100%;\r\n}\r\n\r\n.ZestButton-module_sm__G1vAP {\r\n font-size: 0.85rem;\r\n padding: 0.4em 0.8em;\r\n}\r\n\r\n.ZestButton-module_md__Y-PMO {\r\n font-size: 1rem;\r\n padding: 0.6em 1.2em;\r\n}\r\n\r\n.ZestButton-module_lg__AQgdf {\r\n font-size: 1.125rem;\r\n padding: 0.8em 1.6em;\r\n}\r\n\r\n.ZestButton-module_inner__1j2Fr {\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n position: relative;\r\n}\r\n\r\n.ZestButton-module_spinner__l2hLe {\r\n position: absolute;\r\n width: 1em;\r\n height: 1em;\r\n animation: ZestButton-module_spin__4asdw 0.75s linear infinite;\r\n color: currentColor; /* Inherit color from button text */\r\n}\r\n\r\n.ZestButton-module_content__hlea3 {\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n gap: 0.5em;\r\n}\r\n\r\n.ZestButton-module_label__8x263 {\r\n display: inline-block;\r\n}\r\n\r\n.ZestButton-module_icon__B3DFi {\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n}\r\n\r\n/* Success + Fail Icons */\r\n.ZestButton-module_animatedCheck__8K4K- {\r\n width: 1.25em;\r\n height: 1.25em;\r\n color: var(--zest-color-icon-success);\r\n animation: ZestButton-module_fadeIn__iEave 0.25s ease-out;\r\n}\r\n\r\n.ZestButton-module_animatedX__KQnt7 {\r\n width: 1.25em;\r\n height: 1.25em;\r\n color: var(--zest-color-icon-fail);\r\n animation: ZestButton-module_fadeIn__iEave 0.25s ease-out;\r\n}\r\n\r\n/* Stroke animations */\r\n.ZestButton-module_animatedCheck__8K4K- path {\r\n stroke-dasharray: 24;\r\n stroke-dashoffset: 24;\r\n animation: ZestButton-module_drawCheck__3DyjT 0.4s ease-out forwards;\r\n}\r\n\r\n.ZestButton-module_animatedX__KQnt7 line {\r\n stroke-dasharray: 24;\r\n stroke-dashoffset: 24;\r\n animation: ZestButton-module_drawCheck__3DyjT 0.4s ease-out forwards;\r\n}\r\n\r\n/* New: apply spacing between left icon and text */\r\n.ZestButton-module_icon__B3DFi:first-child {\r\n margin-right: 0.7em;\r\n}\r\n\r\n/* Optional override for iconRight if needed */\r\n.ZestButton-module_icon__B3DFi:last-child {\r\n margin-left: 0.7em;\r\n}\r\n\r\n/* Animations */\r\n@keyframes ZestButton-module_spin__4asdw {\r\n to {\r\n transform: rotate(360deg);\r\n }\r\n}\r\n\r\n@keyframes ZestButton-module_drawCheck__3DyjT {\r\n to {\r\n stroke-dashoffset: 0;\r\n }\r\n}\r\n\r\n@keyframes ZestButton-module_fadeIn__iEave {\r\n from {\r\n opacity: 0;\r\n transform: scale(0.8);\r\n }\r\n to {\r\n opacity: 1;\r\n transform: scale(1);\r\n }\r\n}\r\n\r\n@keyframes ZestButton-module_shakeIt__ox-R3 {\r\n 0%,\r\n 100% {\r\n transform: translateX(0);\r\n }\r\n 20%,\r\n 60% {\r\n transform: translateX(-4px);\r\n }\r\n 40%,\r\n 80% {\r\n transform: translateX(4px);\r\n }\r\n}\r\n\r\n.ZestButton-module_shake__NtIjf {\r\n animation: ZestButton-module_shakeIt__ox-R3 0.4s ease;\r\n box-shadow: 0 0 0 2px rgba(239, 68, 68, 0.6);\r\n}\r\n\r\n/* Mobile-first adjustments for hit area */\r\n@media (max-width: 768px) {\r\n .ZestButton-module_button__KDafc {\r\n min-height: 44px; /* Ensure a minimum touch target size for accessibility */\r\n }\r\n\r\n .ZestButton-module_sm__G1vAP {\r\n padding: 0.6em 1em; /* Increase padding for small buttons on mobile */\r\n }\r\n\r\n .ZestButton-module_md__Y-PMO {\r\n padding: 0.8em 1.4em; /* Slightly increase padding for medium buttons on mobile */\r\n }\r\n}\r\n\r\n/* ------------------------------------------------------------------ */\r\n/* Split button (zest.dropdownOptions) */\r\n/* ------------------------------------------------------------------ */\r\n\r\n.ZestButton-module_splitButtonContainer__UPOGd {\r\n display: inline-flex;\r\n align-items: stretch;\r\n}\r\n\r\n.ZestButton-module_splitButtonMain__F0cHX {\r\n border-top-right-radius: 0;\r\n border-bottom-right-radius: 0;\r\n border-right: none;\r\n}\r\n\r\n.ZestButton-module_dropdownTrigger__wqBMN {\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n border-top-left-radius: 0;\r\n border-bottom-left-radius: 0;\r\n padding: 0 0.7em;\r\n min-width: 2.4em;\r\n}\r\n\r\n/* Divider between the two split-button segments, defined for all four\r\n buttonStyles (not just solid/text) so the seam is always visible at rest.\r\n Each dropdownTrigger also gets a persistent (not just on-hover) background\r\n tint so the chevron reads as its own distinct, separately-clickable\r\n region — this is the primary fix for the two segments blending together. */\r\n.ZestButton-module_dropdownTrigger__wqBMN.ZestButton-module_solid__cu4tr {\r\n border-left: 1px solid rgba(255, 255, 255, 0.35);\r\n background-color: rgba(0, 0, 0, 0.06);\r\n}\r\n.ZestButton-module_dropdownTrigger__wqBMN.ZestButton-module_solid__cu4tr:hover:not(:disabled) {\r\n background-color: rgba(0, 0, 0, 0.12);\r\n}\r\n.ZestButton-module_dropdownTrigger__wqBMN.ZestButton-module_text__8X1xD {\r\n border-left: 1px solid rgba(0, 0, 0, 0.18);\r\n background-color: rgba(0, 0, 0, 0.035);\r\n}\r\n.ZestButton-module_dropdownTrigger__wqBMN.ZestButton-module_outline__esgLq,\r\n.ZestButton-module_dropdownTrigger__wqBMN.ZestButton-module_dashed__ebKYF {\r\n /* outline/dashed already draw their own full border; splitButtonMain\r\n removes its own right border below so only this left border remains\r\n as the seam, keeping its weight consistent with solid/text. */\r\n border-left-width: 2px;\r\n background-color: rgba(0, 0, 0, 0.035);\r\n}\r\n\r\n.ZestButton-module_splitButtonMain__F0cHX.ZestButton-module_outline__esgLq,\r\n.ZestButton-module_splitButtonMain__F0cHX.ZestButton-module_dashed__ebKYF {\r\n border-right: none;\r\n}\r\n\r\n.ZestButton-module_force-dark__cx74D .ZestButton-module_dropdownTrigger__wqBMN.ZestButton-module_solid__cu4tr {\r\n background-color: rgba(255, 255, 255, 0.1);\r\n}\r\n.ZestButton-module_force-dark__cx74D .ZestButton-module_dropdownTrigger__wqBMN.ZestButton-module_solid__cu4tr:hover:not(:disabled) {\r\n background-color: rgba(255, 255, 255, 0.18);\r\n}\r\n.ZestButton-module_force-dark__cx74D .ZestButton-module_dropdownTrigger__wqBMN.ZestButton-module_text__8X1xD,\r\n.ZestButton-module_force-dark__cx74D .ZestButton-module_dropdownTrigger__wqBMN.ZestButton-module_outline__esgLq,\r\n.ZestButton-module_force-dark__cx74D .ZestButton-module_dropdownTrigger__wqBMN.ZestButton-module_dashed__ebKYF {\r\n background-color: rgba(255, 255, 255, 0.06);\r\n}\r\n\r\n.ZestButton-module_dropdownChevron__GMkdW {\r\n width: 0.85em;\r\n height: 0.85em;\r\n transition: transform var(--zest-motion-base) var(--zest-ease-spring);\r\n}\r\n\r\n.ZestButton-module_dropdownChevronOpen__-2CEj {\r\n transform: rotate(180deg);\r\n}\r\n\r\n.ZestButton-module_dropdownMenuContent__wIHv- {\r\n min-width: 180px;\r\n width: max-content;\r\n max-width: 320px;\r\n padding: 0.4rem;\r\n border-radius: var(--zest-radius-md);\r\n background: var(--zest-color-menu-bg, #ffffff);\r\n color: var(--zest-color-menu-text, #1f2937);\r\n box-shadow: var(--zest-shadow-menu);\r\n border: 1px solid rgba(0, 0, 0, 0.08);\r\n z-index: 1000;\r\n transform-origin: var(--radix-dropdown-menu-content-transform-origin);\r\n}\r\n\r\n.ZestButton-module_dropdownMenuContent__wIHv-[data-state=\"open\"] {\r\n animation: ZestButton-module_dropdownMenuIn__st1Yy var(--zest-motion-base) var(--zest-ease-standard);\r\n}\r\n/* Radix unmounts Content immediately on close (no forceMount is used here),\r\n so this closing keyframe may not get a chance to visibly play in every\r\n browser — treat it as a graceful enhancement, not a guarantee. */\r\n.ZestButton-module_dropdownMenuContent__wIHv-[data-state=\"closed\"] {\r\n animation: ZestButton-module_dropdownMenuOut__o45vS var(--zest-motion-fast) var(--zest-ease-standard);\r\n}\r\n\r\n@keyframes ZestButton-module_dropdownMenuIn__st1Yy {\r\n from { opacity: 0; transform: scale(0.96); }\r\n to { opacity: 1; transform: scale(1); }\r\n}\r\n@keyframes ZestButton-module_dropdownMenuOut__o45vS {\r\n from { opacity: 1; transform: scale(1); }\r\n to { opacity: 0; transform: scale(0.96); }\r\n}\r\n\r\n@media (prefers-reduced-motion: reduce) {\r\n .ZestButton-module_dropdownMenuContent__wIHv-[data-state=\"open\"],\r\n .ZestButton-module_dropdownMenuContent__wIHv-[data-state=\"closed\"] {\r\n animation: none;\r\n }\r\n .ZestButton-module_dropdownChevron__GMkdW {\r\n transition: none;\r\n }\r\n .ZestButton-module_button__KDafc {\r\n transition: background-color var(--zest-motion-base) ease,\r\n box-shadow var(--zest-motion-base) ease,\r\n color var(--zest-motion-base) ease,\r\n border-color var(--zest-motion-base) ease;\r\n }\r\n .ZestButton-module_button__KDafc:hover:not(:disabled),\r\n .ZestButton-module_button__KDafc:active:not(:disabled) {\r\n transform: none;\r\n }\r\n}\r\n\r\n.ZestButton-module_force-dark__cx74D.ZestButton-module_dropdownMenuContent__wIHv-,\r\n.ZestButton-module_dropdownMenuContent__wIHv-.ZestButton-module_force-dark__cx74D {\r\n background: #1f2937;\r\n color: #f3f4f6;\r\n border-color: rgba(255, 255, 255, 0.12);\r\n}\r\n\r\n.ZestButton-module_dropdownMenuItem__7vFFl {\r\n display: flex;\r\n align-items: center;\r\n gap: 0.5em;\r\n padding: 0.55em 0.75em;\r\n border-radius: var(--zest-radius-sm);\r\n cursor: pointer;\r\n outline: none;\r\n font-family: \"Segoe UI\", Roboto, sans-serif;\r\n font-size: 0.9rem;\r\n}\r\n\r\n.ZestButton-module_dropdownMenuItem__7vFFl:hover,\r\n.ZestButton-module_dropdownMenuItem__7vFFl[data-highlighted] {\r\n background: rgba(0, 0, 0, 0.06);\r\n}\r\n\r\n.ZestButton-module_force-dark__cx74D .ZestButton-module_dropdownMenuItem__7vFFl:hover,\r\n.ZestButton-module_force-dark__cx74D .ZestButton-module_dropdownMenuItem__7vFFl[data-highlighted] {\r\n background: rgba(255, 255, 255, 0.1);\r\n}\r\n\r\n.ZestButton-module_dropdownMenuItem__7vFFl.ZestButton-module_disabled__gw6y3,\r\n.ZestButton-module_dropdownMenuItem__7vFFl[data-disabled] {\r\n opacity: 0.5;\r\n cursor: not-allowed;\r\n}\r\n\r\n.ZestButton-module_dropdownMenuItemLabel__ArKh6 {\r\n flex: 1;\r\n}\r\n\r\n/* Mobile-first: chevron segment always meets the 44px touch-target minimum,\r\n independent of `size`, since it's a narrow tap target next to another one. */\r\n.ZestButton-module_dropdownTrigger__wqBMN {\r\n min-height: 44px;\r\n}\r\n\r\n@media (min-width: 769px) {\r\n /* Desktop-aware: once mis-tapping a neighbouring segment isn't a concern,\r\n let the chevron shrink to match the main button's own sizing. */\r\n .ZestButton-module_dropdownTrigger__wqBMN.ZestButton-module_sm__G1vAP {\r\n min-height: 0;\r\n padding: 0.4em 0.6em;\r\n }\r\n .ZestButton-module_dropdownTrigger__wqBMN.ZestButton-module_md__Y-PMO {\r\n min-height: 0;\r\n padding: 0.6em 0.7em;\r\n }\r\n .ZestButton-module_dropdownTrigger__wqBMN.ZestButton-module_lg__AQgdf {\r\n min-height: 0;\r\n padding: 0.8em 0.9em;\r\n }\r\n}\r\n\r\n";
|
|
1389
|
+
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","stretch":"ZestButton-module_stretch__CTLNw","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","splitButtonContainer":"ZestButton-module_splitButtonContainer__UPOGd","splitButtonMain":"ZestButton-module_splitButtonMain__F0cHX","dropdownTrigger":"ZestButton-module_dropdownTrigger__wqBMN","dropdownChevron":"ZestButton-module_dropdownChevron__GMkdW","dropdownChevronOpen":"ZestButton-module_dropdownChevronOpen__-2CEj","dropdownMenuContent":"ZestButton-module_dropdownMenuContent__wIHv-","dropdownMenuIn":"ZestButton-module_dropdownMenuIn__st1Yy","dropdownMenuOut":"ZestButton-module_dropdownMenuOut__o45vS","dropdownMenuItem":"ZestButton-module_dropdownMenuItem__7vFFl","dropdownMenuItemLabel":"ZestButton-module_dropdownMenuItemLabel__ArKh6"};
|
|
1389
1390
|
styleInject(css_248z);
|
|
1390
1391
|
|
|
1391
1392
|
// Create the Zest Context with a default empty configuration
|
|
@@ -1738,12 +1739,139 @@ const useThemeDetection = () => {
|
|
|
1738
1739
|
return systemTheme;
|
|
1739
1740
|
};
|
|
1740
1741
|
|
|
1742
|
+
const AnimatedCheckmark$1 = () => (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$1 = () => (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 ZestDropdownMenuItem = ({ option, disabled: externallyDisabled = false, onBusyChange, }) => {
|
|
1745
|
+
const { label, icon, disabled: optionDisabled = false, onClick, busyOptions = {}, successOptions = {}, confirmOptions, } = option;
|
|
1746
|
+
const { handleInternally, minBusyDurationMs, showCheckmark, showFailIcon, internalBusy, wasSuccessful, wasFailed, startBusy, endBusy, } = useBusyState({ busyOptions, successOptions });
|
|
1747
|
+
const { awaitingConfirm, currentChildren, startConfirmation, stopConfirmation } = useConfirmation({
|
|
1748
|
+
confirmOptions,
|
|
1749
|
+
originalChildren: label,
|
|
1750
|
+
onConfirmFail: () => endBusy(false),
|
|
1751
|
+
});
|
|
1752
|
+
useEffect(() => {
|
|
1753
|
+
onBusyChange?.(internalBusy);
|
|
1754
|
+
}, [internalBusy, onBusyChange]);
|
|
1755
|
+
const isDisabled = externallyDisabled || optionDisabled || internalBusy;
|
|
1756
|
+
const handleActivate = async (e) => {
|
|
1757
|
+
if (handleInternally && typeof onClick === "function") {
|
|
1758
|
+
try {
|
|
1759
|
+
startBusy();
|
|
1760
|
+
const startTime = Date.now();
|
|
1761
|
+
await onClick(e);
|
|
1762
|
+
const elapsed = Date.now() - startTime;
|
|
1763
|
+
const remaining = Math.max(minBusyDurationMs - elapsed, 0);
|
|
1764
|
+
if (remaining > 0) {
|
|
1765
|
+
await new Promise((resolve) => setTimeout(resolve, remaining));
|
|
1766
|
+
}
|
|
1767
|
+
endBusy(true);
|
|
1768
|
+
}
|
|
1769
|
+
catch (err) {
|
|
1770
|
+
console.error(err);
|
|
1771
|
+
endBusy(false);
|
|
1772
|
+
}
|
|
1773
|
+
}
|
|
1774
|
+
else if (onClick) {
|
|
1775
|
+
onClick(e);
|
|
1776
|
+
}
|
|
1777
|
+
};
|
|
1778
|
+
const handleSelect = (e) => {
|
|
1779
|
+
if (isDisabled) {
|
|
1780
|
+
e.preventDefault();
|
|
1781
|
+
return;
|
|
1782
|
+
}
|
|
1783
|
+
if (awaitingConfirm) {
|
|
1784
|
+
// Keep the menu open through the confirm step; only the real
|
|
1785
|
+
// activation (below) decides whether the menu is allowed to close.
|
|
1786
|
+
e.preventDefault();
|
|
1787
|
+
stopConfirmation();
|
|
1788
|
+
void handleActivate(e);
|
|
1789
|
+
return;
|
|
1790
|
+
}
|
|
1791
|
+
if (confirmOptions) {
|
|
1792
|
+
e.preventDefault();
|
|
1793
|
+
if (!onClick) {
|
|
1794
|
+
console.warn("ZestButton dropdown item: 'confirmOptions' provided but 'onClick' is missing.");
|
|
1795
|
+
}
|
|
1796
|
+
startConfirmation();
|
|
1797
|
+
return;
|
|
1798
|
+
}
|
|
1799
|
+
if (handleInternally) {
|
|
1800
|
+
// Busy items keep the row open to show progress; Radix's default
|
|
1801
|
+
// close-on-select would hide the spinner/checkmark/shake feedback.
|
|
1802
|
+
e.preventDefault();
|
|
1803
|
+
void handleActivate(e);
|
|
1804
|
+
return;
|
|
1805
|
+
}
|
|
1806
|
+
// Plain synchronous item: let Radix close the menu as it normally would.
|
|
1807
|
+
void handleActivate(e);
|
|
1808
|
+
};
|
|
1809
|
+
const renderIcon = () => {
|
|
1810
|
+
if (internalBusy) {
|
|
1811
|
+
return (jsxRuntimeExports.jsx("span", { className: `${styles.icon} ${styles.fadeIn}`, children: jsxRuntimeExports.jsx(FaSpinner, { className: styles.spinner }) }));
|
|
1812
|
+
}
|
|
1813
|
+
if (wasSuccessful && showCheckmark) {
|
|
1814
|
+
return (jsxRuntimeExports.jsx("span", { className: `${styles.icon} ${styles.fadeIn}`, children: jsxRuntimeExports.jsx(AnimatedCheckmark$1, {}) }));
|
|
1815
|
+
}
|
|
1816
|
+
if (wasFailed && showFailIcon) {
|
|
1817
|
+
return (jsxRuntimeExports.jsx("span", { className: `${styles.icon} ${styles.fadeIn}`, children: jsxRuntimeExports.jsx(AnimatedX$1, {}) }));
|
|
1818
|
+
}
|
|
1819
|
+
if (icon) {
|
|
1820
|
+
return jsxRuntimeExports.jsx("span", { className: styles.icon, children: icon });
|
|
1821
|
+
}
|
|
1822
|
+
return null;
|
|
1823
|
+
};
|
|
1824
|
+
return (jsxRuntimeExports.jsxs(DropdownMenu.Item, { className: [
|
|
1825
|
+
styles.dropdownMenuItem,
|
|
1826
|
+
isDisabled ? styles.disabled : "",
|
|
1827
|
+
wasFailed ? styles.shake : "",
|
|
1828
|
+
]
|
|
1829
|
+
.join(" ")
|
|
1830
|
+
.trim(), disabled: isDisabled, onSelect: handleSelect, children: [renderIcon(), jsxRuntimeExports.jsx("span", { className: styles.dropdownMenuItemLabel, children: currentChildren })] }));
|
|
1831
|
+
};
|
|
1832
|
+
|
|
1833
|
+
const ZestDropdownMenu = ({ options, ariaLabel, open, onOpenChange, disabled = false, onAnyItemBusyChange, variant, size, buttonStyle, effectiveTheme, effectiveMenuTheme, menuMinWidth, }) => {
|
|
1834
|
+
const resolvedMenuTheme = effectiveMenuTheme ?? effectiveTheme;
|
|
1835
|
+
const busyKeysRef = useRef(new Set());
|
|
1836
|
+
const handleItemBusyChange = useCallback((key, busy) => {
|
|
1837
|
+
const busyKeys = busyKeysRef.current;
|
|
1838
|
+
const wasAnyBusy = busyKeys.size > 0;
|
|
1839
|
+
if (busy) {
|
|
1840
|
+
busyKeys.add(key);
|
|
1841
|
+
}
|
|
1842
|
+
else {
|
|
1843
|
+
busyKeys.delete(key);
|
|
1844
|
+
}
|
|
1845
|
+
const isAnyBusy = busyKeys.size > 0;
|
|
1846
|
+
if (isAnyBusy !== wasAnyBusy) {
|
|
1847
|
+
onAnyItemBusyChange?.(isAnyBusy);
|
|
1848
|
+
}
|
|
1849
|
+
}, [onAnyItemBusyChange]);
|
|
1850
|
+
return (jsxRuntimeExports.jsxs(DropdownMenu.Root, { open: open, onOpenChange: onOpenChange, modal: false, children: [jsxRuntimeExports.jsx(DropdownMenu.Trigger, { asChild: true, disabled: disabled, children: jsxRuntimeExports.jsx("button", { type: "button", "aria-label": ariaLabel, disabled: disabled, className: [
|
|
1851
|
+
styles.dropdownTrigger,
|
|
1852
|
+
styles[buttonStyle],
|
|
1853
|
+
styles[variant],
|
|
1854
|
+
styles[size],
|
|
1855
|
+
disabled ? styles.disabled : "",
|
|
1856
|
+
effectiveTheme === "light" ? styles["force-light"] : styles["force-dark"],
|
|
1857
|
+
]
|
|
1858
|
+
.join(" ")
|
|
1859
|
+
.trim(), children: jsxRuntimeExports.jsx(FaChevronDown, { className: [styles.dropdownChevron, open ? styles.dropdownChevronOpen : ""]
|
|
1860
|
+
.join(" ")
|
|
1861
|
+
.trim() }) }) }), jsxRuntimeExports.jsx(DropdownMenu.Portal, { children: jsxRuntimeExports.jsx(DropdownMenu.Content, { className: [
|
|
1862
|
+
styles.dropdownMenuContent,
|
|
1863
|
+
resolvedMenuTheme === "light" ? styles["force-light"] : styles["force-dark"],
|
|
1864
|
+
].join(" "), style: menuMinWidth !== undefined
|
|
1865
|
+
? { minWidth: typeof menuMinWidth === "number" ? `${menuMinWidth}px` : menuMinWidth }
|
|
1866
|
+
: undefined, sideOffset: 4, align: "end", collisionPadding: 8, children: options.map((option, index) => (jsxRuntimeExports.jsx(ZestDropdownMenuItem, { option: option, disabled: disabled, onBusyChange: (busy) => handleItemBusyChange(option.key ?? index, busy) }, option.key ?? index))) }) })] }));
|
|
1867
|
+
};
|
|
1868
|
+
|
|
1741
1869
|
// --- Components ---
|
|
1742
1870
|
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
1871
|
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
1872
|
const ZestButton = ({ className = "", disabled, children, onClick, zest: localZestProps, ...props }) => {
|
|
1745
1873
|
const effectiveZestConfig = useZestConfig(localZestProps);
|
|
1746
|
-
const { visualOptions = {}, busyOptions = {}, successOptions = {}, confirmOptions, isDefault = false, theme = 'system', buttonStyle = 'solid', } = effectiveZestConfig;
|
|
1874
|
+
const { visualOptions = {}, busyOptions = {}, successOptions = {}, confirmOptions, isDefault = false, theme = 'system', buttonStyle = 'solid', dropdownOptions, dropdownAriaLabel = "More options", dropdownTheme, dropdownWidth, } = effectiveZestConfig;
|
|
1747
1875
|
const { variant = "standard", size = "md", stretch = false, iconLeft, iconRight, } = visualOptions;
|
|
1748
1876
|
const { handleInternally, minBusyDurationMs, preventRageClick, showCheckmark, showFailIcon, internalBusy, wasSuccessful, wasFailed, startBusy, endBusy, } = useBusyState({ busyOptions, successOptions });
|
|
1749
1877
|
const { awaitingConfirm, currentChildren, startConfirmation, stopConfirmation, } = useConfirmation({
|
|
@@ -1754,6 +1882,38 @@ const ZestButton = ({ className = "", disabled, children, onClick, zest: localZe
|
|
|
1754
1882
|
const buttonRef = useRef(null);
|
|
1755
1883
|
const systemTheme = useThemeDetection();
|
|
1756
1884
|
const effectiveTheme = theme === 'system' ? systemTheme : theme;
|
|
1885
|
+
// Dropdown menus default to light regardless of the button's own theme
|
|
1886
|
+
// (product preference) — dropdownTheme only kicks in once set explicitly.
|
|
1887
|
+
const effectiveDropdownTheme = dropdownTheme
|
|
1888
|
+
? dropdownTheme === 'system'
|
|
1889
|
+
? systemTheme
|
|
1890
|
+
: dropdownTheme
|
|
1891
|
+
: 'light';
|
|
1892
|
+
const hasDropdown = Boolean(dropdownOptions && dropdownOptions.length > 0);
|
|
1893
|
+
const [dropdownOpen, setDropdownOpen] = useState(false);
|
|
1894
|
+
const [anyDropdownItemBusy, setAnyDropdownItemBusy] = useState(false);
|
|
1895
|
+
const splitContainerRef = useRef(null);
|
|
1896
|
+
const [measuredControlWidth, setMeasuredControlWidth] = useState(undefined);
|
|
1897
|
+
useLayoutEffect(() => {
|
|
1898
|
+
if (!hasDropdown || !splitContainerRef.current)
|
|
1899
|
+
return;
|
|
1900
|
+
const el = splitContainerRef.current;
|
|
1901
|
+
// A width of 0 means "not actually laid out yet" (e.g. jsdom, or a
|
|
1902
|
+
// display:none ancestor) rather than a real zero-width control — treat
|
|
1903
|
+
// it as unavailable so the menu falls back to its own CSS default
|
|
1904
|
+
// instead of being forced to a bogus 0px minimum.
|
|
1905
|
+
const initialWidth = el.getBoundingClientRect().width;
|
|
1906
|
+
if (initialWidth > 0)
|
|
1907
|
+
setMeasuredControlWidth(initialWidth);
|
|
1908
|
+
const observer = new ResizeObserver((entries) => {
|
|
1909
|
+
const width = entries[0]?.contentRect.width ?? 0;
|
|
1910
|
+
if (width > 0)
|
|
1911
|
+
setMeasuredControlWidth(width);
|
|
1912
|
+
});
|
|
1913
|
+
observer.observe(el);
|
|
1914
|
+
return () => observer.disconnect();
|
|
1915
|
+
}, [hasDropdown]);
|
|
1916
|
+
const resolvedMenuMinWidth = dropdownWidth ?? measuredControlWidth;
|
|
1757
1917
|
const effectiveBusy = typeof props["aria-busy"] === "boolean"
|
|
1758
1918
|
? Boolean(props["aria-busy"])
|
|
1759
1919
|
: handleInternally
|
|
@@ -1761,7 +1921,8 @@ const ZestButton = ({ className = "", disabled, children, onClick, zest: localZe
|
|
|
1761
1921
|
: false;
|
|
1762
1922
|
const isDisabled = disabled ||
|
|
1763
1923
|
effectiveBusy ||
|
|
1764
|
-
(preventRageClick && (wasSuccessful || wasFailed))
|
|
1924
|
+
(preventRageClick && (wasSuccessful || wasFailed)) ||
|
|
1925
|
+
anyDropdownItemBusy;
|
|
1765
1926
|
const handleClick = async (e) => {
|
|
1766
1927
|
if (preventRageClick && internalBusy)
|
|
1767
1928
|
return;
|
|
@@ -1803,7 +1964,7 @@ const ZestButton = ({ className = "", disabled, children, onClick, zest: localZe
|
|
|
1803
1964
|
}
|
|
1804
1965
|
};
|
|
1805
1966
|
useEffect(() => {
|
|
1806
|
-
if (!isDefault || isDisabled)
|
|
1967
|
+
if (!isDefault || isDisabled || dropdownOpen)
|
|
1807
1968
|
return;
|
|
1808
1969
|
const listener = (e) => {
|
|
1809
1970
|
const target = e.target;
|
|
@@ -1820,7 +1981,7 @@ const ZestButton = ({ className = "", disabled, children, onClick, zest: localZe
|
|
|
1820
1981
|
};
|
|
1821
1982
|
document.addEventListener("keydown", listener);
|
|
1822
1983
|
return () => document.removeEventListener("keydown", listener);
|
|
1823
|
-
}, [isDefault, isDisabled]);
|
|
1984
|
+
}, [isDefault, isDisabled, dropdownOpen]);
|
|
1824
1985
|
const renderLeftIcon = () => {
|
|
1825
1986
|
if (effectiveBusy) {
|
|
1826
1987
|
return (jsxRuntimeExports.jsx("span", { className: `${styles.icon} ${styles.fadeIn}`, children: jsxRuntimeExports.jsx(FaSpinner, { className: styles.spinner }) }));
|
|
@@ -1836,7 +1997,7 @@ const ZestButton = ({ className = "", disabled, children, onClick, zest: localZe
|
|
|
1836
1997
|
}
|
|
1837
1998
|
return null;
|
|
1838
1999
|
};
|
|
1839
|
-
|
|
2000
|
+
const buttonElement = (jsxRuntimeExports.jsx("button", { ref: buttonRef, className: [
|
|
1840
2001
|
styles.button,
|
|
1841
2002
|
styles[buttonStyle],
|
|
1842
2003
|
styles[variant],
|
|
@@ -1845,8 +2006,13 @@ const ZestButton = ({ className = "", disabled, children, onClick, zest: localZe
|
|
|
1845
2006
|
isDisabled ? styles.disabled : "",
|
|
1846
2007
|
wasFailed ? styles.shake : "",
|
|
1847
2008
|
effectiveTheme === 'light' ? styles['force-light'] : styles['force-dark'],
|
|
2009
|
+
hasDropdown ? styles.splitButtonMain : "",
|
|
1848
2010
|
className,
|
|
1849
2011
|
].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 })] })] }) }));
|
|
2012
|
+
if (!hasDropdown) {
|
|
2013
|
+
return buttonElement;
|
|
2014
|
+
}
|
|
2015
|
+
return (jsxRuntimeExports.jsxs("div", { className: styles.splitButtonContainer, ref: splitContainerRef, children: [buttonElement, jsxRuntimeExports.jsx(ZestDropdownMenu, { options: dropdownOptions, ariaLabel: dropdownAriaLabel, open: dropdownOpen, onOpenChange: setDropdownOpen, disabled: isDisabled, onAnyItemBusyChange: setAnyDropdownItemBusy, variant: variant, size: size, buttonStyle: buttonStyle, effectiveTheme: effectiveTheme, effectiveMenuTheme: effectiveDropdownTheme, menuMinWidth: resolvedMenuMinWidth })] }));
|
|
1850
2016
|
};
|
|
1851
2017
|
|
|
1852
2018
|
const ZestButtonConfigProvider = ({ config, children }) => {
|