@veracity/vui 4.0.0-alpha.366771.2602181640 → 4.0.0-alpha.375407.2602201014
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/button/button.d.ts +3 -3
- package/dist/button/button.d.ts.map +1 -1
- package/dist/button/button.js +229 -169
- package/dist/button/button.js.map +1 -1
- package/dist/button/buttonIcon.js +12 -3
- package/dist/button/buttonIcon.js.map +1 -1
- package/dist/button/consts.js +3 -3
- package/dist/button/consts.js.map +1 -1
- package/dist/button/theme.js +155 -166
- package/dist/button/theme.js.map +1 -1
- package/dist/buttonGroup/buttonGroup.d.ts.map +1 -1
- package/dist/buttonGroup/buttonGroup.js +2 -18
- package/dist/buttonGroup/buttonGroup.js.map +1 -1
- package/dist/core/index.d.ts +1 -1
- package/dist/core/links.d.ts.map +1 -1
- package/dist/core/links.js +4 -5
- package/dist/core/links.js.map +1 -1
- package/dist/core/vuiProvider/consts.d.ts +49 -1
- package/dist/core/vuiProvider/consts.d.ts.map +1 -1
- package/dist/core/vuiProvider/consts.js +651 -1
- package/dist/core/vuiProvider/consts.js.map +1 -1
- package/dist/core/vuiProvider/globalStyle.js +3 -1
- package/dist/core/vuiProvider/globalStyle.js.map +1 -1
- package/dist/core/vuiProvider/index.d.ts +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.js +3 -2
- package/dist/link/link.d.ts.map +1 -1
- package/dist/link/link.js +3 -2
- package/dist/link/link.js.map +1 -1
- package/dist/message/consts.js +4 -4
- package/dist/message/consts.js.map +1 -1
- package/dist/notification/consts.js +3 -14
- package/dist/notification/consts.js.map +1 -1
- package/dist/notification/index.d.ts +1 -0
- package/dist/notification/notification.d.ts +2 -0
- package/dist/notification/notification.d.ts.map +1 -1
- package/dist/notification/notification.js +18 -10
- package/dist/notification/notification.js.map +1 -1
- package/dist/notification/notification.types.d.ts +4 -3
- package/dist/notification/notification.types.d.ts.map +1 -1
- package/dist/notification/notificationAction.d.ts +12 -0
- package/dist/notification/notificationAction.d.ts.map +1 -0
- package/dist/notification/notificationAction.js +37 -0
- package/dist/notification/notificationAction.js.map +1 -0
- package/dist/notification/notificationButton.d.ts.map +1 -1
- package/dist/notification/notificationButton.js +1 -13
- package/dist/notification/notificationButton.js.map +1 -1
- package/dist/notification/notificationIcon.js +0 -1
- package/dist/notification/notificationIcon.js.map +1 -1
- package/dist/notification/notificationText.js +0 -5
- package/dist/notification/notificationText.js.map +1 -1
- package/dist/notification/theme.js +119 -43
- package/dist/notification/theme.js.map +1 -1
- package/dist/prose/prose.js +1 -1
- package/dist/prose/prose.js.map +1 -1
- package/dist/theme/components.d.ts +85 -18
- package/dist/theme/defaultTheme.d.ts +85 -18
- package/package.json +1 -1
package/dist/button/button.d.ts
CHANGED
|
@@ -44,11 +44,11 @@ declare const ButtonBase: styled_components0.StyledComponent<"button", styled_co
|
|
|
44
44
|
text?: React.ReactNode;
|
|
45
45
|
}, never>;
|
|
46
46
|
/**
|
|
47
|
-
*
|
|
47
|
+
* Button Component
|
|
48
48
|
*
|
|
49
|
-
*
|
|
49
|
+
* A versatile button component that supports multiple states, sizes, variants, and behaviors.
|
|
50
|
+
* Part of the VUI design system with built-in accessibility features.
|
|
50
51
|
*
|
|
51
|
-
* Exposes some props to the children via context.
|
|
52
52
|
*/
|
|
53
53
|
declare const Button: VuiComponent<"button", ButtonProps> & {
|
|
54
54
|
Icon: typeof ButtonIcon;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"button.d.ts","names":[],"sources":["../../src/button/button.tsx"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"button.d.ts","names":[],"sources":["../../src/button/button.tsx"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;cAiFa,UAAA,qBAAU,eAAA,WAmCtB,kBAAA,CAnCsB,YAAA,EAAA,cAAA,GAAA,oBAAA,GAAA,WAAA,GAAA,cAAA,GAAA,gBAAA,GAAA,iBAAA,GAAA,eAAA,GAAA,gBAAA,GAAA,mBAAA,GAAA,qBAAA,GAAA,sBAAA,GAAA,oBAAA,GAAA,gBAAA,GAAA,mBAAA,GAAA,qBAAA,GAAA,sBAAA,GAAA,oBAAA,GAAA,gBAAA,GAAA,iBAAA,GAAA,YAAA,GAAA,iBAAA,GAAA,iBAAA,GAAA,iBAAA,GAAA,YAAA,GAAA,YAAA,GAAA,mBAAA,GAAA,mBAAA,GAAA,gBAAA,GAAA,gBAAA,GAAA,SAAA,GAAA,cAAA,GAAA,YAAA,GAAA,cAAA,GAAA,eAAA,GAAA,YAAA,GAAA,eAAA,GAAA,iBAAA,GAAA,mBAAA,GAAA,iBAAA,GAAA,aAAA,GAAA,aAAA,GAAA,eAAA,GAAA,cAAA,GAAA,kBAAA,GAAA,SAAA,GAAA,gBAAA,GAAA,cAAA,GAAA,UAAA,GAAA,QAAA,GAAA,QAAA,GAAA,QAAA,GAAA,cAAA,GAAA,WAAA,GAAA,eAAA,GAAA,YAAA,GAAA,iBAAA,GAAA,oBAAA,GAAA,iBAAA,GAAA,wBAAA,GAAA,qBAAA,GAAA,sBAAA,GAAA,aAAA,GAAA,eAAA,GAAA,WAAA,GAAA,kBAAA,GAAA,WAAA,GAAA,eAAA,GAAA,gBAAA,GAAA,UAAA,GAAA,cAAA,GAAA,aAAA,GAAA,cAAA,GAAA,cAAA,GAAA,aAAA,GAAA,WAAA,GAAA,QAAA,GAAA,UAAA,GAAA,WAAA,GAAA,SAAA,GAAA,eAAA,GAAA,uBAAA,GAAA,cAAA,GAAA,aAAA,GAAA,UAAA,GAAA,WAAA,GAAA,aAAA,GAAA,cAAA,GAAA,aAAA,GAAA,cAAA,GAAA,WAAA,GAAA,cAAA,GAAA,gBAAA,GAAA,iBAAA,GAAA,eAAA,GAAA,YAAA,GAAA,YAAA,GAAA,YAAA,GAAA,eAAA,GAAA,iBAAA,GAAA,kBAAA,GAAA,gBAAA,GAAA,aAAA,GAAA,aAAA,GAAA,WAAA,GAAA,WAAA,GAAA,kBAAA,GAAA,kBAAA,GAAA,mBAAA,GAAA,gBAAA,GAAA,cAAA,GAAA,oBAAA,GAAA,eAAA,GAAA,eAAA,GAAA,WAAA,GAAA,UAAA,GAAA,UAAA,GAAA,UAAA,GAAA,WAAA,GAAA,WAAA,GAAA,eAAA,GAAA,uBAAA,GAAA,uBAAA,GAAA,6BAAA,GAAA,oBAAA,GAAA,eAAA,GAAA,aAAA,GAAA,cAAA,GAAA,eAAA,GAAA,eAAA,GAAA,UAAA,GAAA,kBAAA,GAAA,mBAAA,GAAA,cAAA,GAAA,kBAAA,GAAA,kBAAA,GAAA,eAAA,GAAA,iBAAA,GAAA,kBAAA,GAAA,sBAAA,GAAA,cAAA,GAAA,YAAA;;iBAAA,QAAA;;;;;;;;;;;;;;;;;;;;;;cAmQV,MAAA,EA4KP,YAAA,WAAuB,WAAA;EAC3B,IAAA,SAAa,UAAA;EACb,IAAA,SAAa,UAAA;AAAA"}
|
package/dist/button/button.js
CHANGED
|
@@ -19,6 +19,7 @@ const DEFAULT_BOX_SHADOW = "md";
|
|
|
19
19
|
const DEFAULT_DISABLED_OPACITY = .5;
|
|
20
20
|
const BUTTON_TYPE = "button";
|
|
21
21
|
const FOCUS_RING_SIZE = 3;
|
|
22
|
+
const MENU_FOCUS_BORDER_WIDTH = 3;
|
|
22
23
|
const ContentWrapper = styled.span`
|
|
23
24
|
visibility: hidden;
|
|
24
25
|
`;
|
|
@@ -34,168 +35,203 @@ const ButtonBase = styled.buttonBox`
|
|
|
34
35
|
align-items: center;
|
|
35
36
|
display: inline-flex;
|
|
36
37
|
flex-shrink: 0;
|
|
37
|
-
font-weight: medium;
|
|
38
|
+
font-weight: var(--vui-button-font-weight, medium);
|
|
38
39
|
justify-content: center;
|
|
39
40
|
line-height: normal;
|
|
40
41
|
overflow: hidden;
|
|
41
42
|
position: relative;
|
|
42
|
-
transition-duration: fast;
|
|
43
|
+
transition-duration: var(--vui-button-transition-duration, fast);
|
|
43
44
|
user-select: none;
|
|
44
45
|
width: fit-content;
|
|
45
46
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
&.vui-button-variant-
|
|
52
|
-
|
|
53
|
-
color: #91ffba;
|
|
54
|
-
border-color-left: #91ffba;
|
|
55
|
-
}
|
|
56
|
-
&.vui-button-variant-menuDark.vui-button-variant-menuDark:focus,
|
|
57
|
-
&.vui-button-variant-menuDark.vui-button-variant-menuDark:focus-visible,
|
|
58
|
-
&.vui-button-variant-menuLight.vui-button-variant-menuLight:focus,
|
|
59
|
-
&.vui-button-variant-menuLight.vui-button-variant-menuLight:focus-visible {
|
|
47
|
+
/* Menu variant focus states - default removes focus ring
|
|
48
|
+
* Used when focusAsBorder class is NOT present (standard focus ring behavior)
|
|
49
|
+
*/
|
|
50
|
+
&.vui-button-variant-menuDark:not(.focusAsBorder):focus,
|
|
51
|
+
&.vui-button-variant-menuDark:not(.focusAsBorder):focus-visible,
|
|
52
|
+
&.vui-button-variant-menuLight:not(.focusAsBorder):focus,
|
|
53
|
+
&.vui-button-variant-menuLight:not(.focusAsBorder):focus-visible {
|
|
60
54
|
box-shadow: none;
|
|
61
55
|
outline: none;
|
|
62
|
-
border-bottom: 0 solid transparent;
|
|
63
56
|
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
57
|
+
|
|
58
|
+
/* Menu variant focus states - with border indicator for accessibility
|
|
59
|
+
* Used when focusAsBorder class IS present (alternative focus indication via border)
|
|
60
|
+
* Color: theme menuLight/menuDark.focusBorderBottomColor
|
|
61
|
+
*/
|
|
62
|
+
&.vui-button-variant-menuDark.focusAsBorder:focus,
|
|
63
|
+
&.vui-button-variant-menuDark.focusAsBorder:focus-visible,
|
|
64
|
+
&.vui-button-variant-menuLight.focusAsBorder:focus,
|
|
65
|
+
&.vui-button-variant-menuLight.focusAsBorder:focus-visible {
|
|
68
66
|
transition: border-bottom 0s linear;
|
|
69
|
-
border-bottom:
|
|
67
|
+
border-bottom: ${MENU_FOCUS_BORDER_WIDTH}px solid var(--color-sky-blue-90); /* theme: focusBorderBottomColor */
|
|
70
68
|
}
|
|
71
69
|
`;
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
return {
|
|
80
|
-
styles,
|
|
81
|
-
borderWidth,
|
|
82
|
-
h,
|
|
83
|
-
spaceX,
|
|
84
|
-
buttonStyles
|
|
85
|
-
};
|
|
86
|
-
}, [mergedProps, variant]);
|
|
87
|
-
};
|
|
88
|
-
const extractNumericValue = (value) => {
|
|
70
|
+
/**
|
|
71
|
+
* Extracts a numeric value from various input types (number, string, etc.)
|
|
72
|
+
* @param value - Value to extract number from
|
|
73
|
+
* @param fallback - Default value if extraction fails
|
|
74
|
+
* @returns Extracted number or fallback
|
|
75
|
+
*/
|
|
76
|
+
const extractNumericValue = (value, fallback = DEFAULT_BORDER_WIDTH) => {
|
|
89
77
|
if (typeof value === "number") return value;
|
|
90
78
|
if (typeof value === "string") {
|
|
91
79
|
const parsed = parseInt(value, 10);
|
|
92
|
-
|
|
80
|
+
if (!isNaN(parsed)) return parsed;
|
|
93
81
|
}
|
|
94
|
-
return
|
|
82
|
+
return fallback;
|
|
95
83
|
};
|
|
96
|
-
|
|
84
|
+
/**
|
|
85
|
+
* Consolidated hook for computing all button styles and props.
|
|
86
|
+
* Combines theme resolution, spacing, state-based styles, and className generation.
|
|
87
|
+
* Reduces memoization overhead by computing all derived values in a single pass.
|
|
88
|
+
*
|
|
89
|
+
* @param mergedProps - Combined props from ButtonGroup context and component props
|
|
90
|
+
* @param variant - Resolved variant object with theme overrides
|
|
91
|
+
* @param borderWidthProp - Optional border width override from props
|
|
92
|
+
* @param icon - Main icon (replaces all content when present)
|
|
93
|
+
* @param iconLeft - Left icon slot
|
|
94
|
+
* @param iconRight - Right icon slot
|
|
95
|
+
* @param isLoading - Loading state flag
|
|
96
|
+
* @param isSplit - Deprecated: Split layout mode
|
|
97
|
+
* @param disabled - Disabled state flag
|
|
98
|
+
* @param isActive - Active state flag
|
|
99
|
+
* @param isRound - Round/pill-shaped borders flag
|
|
100
|
+
* @param isElevated - Box shadow elevation flag
|
|
101
|
+
* @param hasState - Whether button has a state overlay
|
|
102
|
+
* @param isFullWidth - Full width layout flag
|
|
103
|
+
* @param className - Additional CSS classes
|
|
104
|
+
* @param variantProp - Variant name for class generation
|
|
105
|
+
* @returns Computed styles, spacing, props, and className
|
|
106
|
+
*/
|
|
107
|
+
const useButtonComputed = (mergedProps, variant, borderWidthProp, icon, iconLeft, iconRight, isLoading, isSplit, disabled, isActive, isRound, isElevated, hasState, isFullWidth, className, variantProp) => {
|
|
108
|
+
const styles = useStyleConfig("Button", {
|
|
109
|
+
...mergedProps,
|
|
110
|
+
...variant
|
|
111
|
+
}) || {};
|
|
97
112
|
return useMemo(() => {
|
|
113
|
+
const container = styles.container || {};
|
|
114
|
+
const { borderWidth = DEFAULT_BORDER_WIDTH, h = 32, spaceX = 0, ...buttonStyles } = container;
|
|
115
|
+
const normalizedStyles = {
|
|
116
|
+
...buttonStyles,
|
|
117
|
+
activeColor: buttonStyles.activeColor ?? buttonStyles.color,
|
|
118
|
+
hoverColor: buttonStyles.hoverColor ?? buttonStyles.color
|
|
119
|
+
};
|
|
98
120
|
const border = extractNumericValue(borderWidthProp) || borderWidth || DEFAULT_BORDER_WIDTH;
|
|
99
121
|
const spaceXItem = spaceX - border;
|
|
100
122
|
const spaceXText = spaceX * 2 - border;
|
|
101
123
|
return {
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
124
|
+
styles,
|
|
125
|
+
borderWidth,
|
|
126
|
+
h,
|
|
127
|
+
spaceX,
|
|
128
|
+
buttonStyles: normalizedStyles,
|
|
129
|
+
spacing: {
|
|
130
|
+
borderWidth: border,
|
|
131
|
+
pl: `var(--vui-button-px-start, ${icon || iconLeft || isLoading ? spaceXItem : spaceXText}px)`,
|
|
132
|
+
pr: `var(--vui-button-px-end, ${icon || iconRight ? spaceXItem : spaceXText}px)`
|
|
133
|
+
},
|
|
134
|
+
disabledProps: disabled ? filterUndefined({
|
|
135
|
+
"aria-disabled": disabled,
|
|
136
|
+
bg: container?.disabledBg ?? void 0,
|
|
137
|
+
borderColor: container?.disabledBorderColor ?? void 0,
|
|
138
|
+
color: container?.disabledColor ?? void 0,
|
|
139
|
+
hoverBg: container?.disabledBg ?? normalizedStyles.bg,
|
|
140
|
+
hoverBorderColor: container?.disabledBorderColor ?? normalizedStyles.borderColor,
|
|
141
|
+
hoverColor: container?.disabledColor ?? normalizedStyles.color,
|
|
142
|
+
activeBg: container?.disabledBg ?? normalizedStyles.bg,
|
|
143
|
+
cursor: "not-allowed",
|
|
144
|
+
opacity: container?.disabledOpacity ?? DEFAULT_DISABLED_OPACITY
|
|
145
|
+
}) : {},
|
|
146
|
+
activeProps: isActive ? filterUndefined({
|
|
147
|
+
bg: container?.activeBg,
|
|
148
|
+
borderColor: container?.activeBorderColor,
|
|
149
|
+
color: container?.activeColor,
|
|
150
|
+
hoverBg: container?.activeBg,
|
|
151
|
+
hoverBorderColor: container?.activeBorderColor,
|
|
152
|
+
hoverColor: container?.activeColor,
|
|
153
|
+
cursor: "default"
|
|
154
|
+
}) : {},
|
|
155
|
+
aliasedProps: filterUndefined({
|
|
156
|
+
borderRadius: isRound ? h / 2 : `var(--vui-button-border-radius, ${container?.borderRadius || 0}px)`,
|
|
157
|
+
boxShadow: isElevated ? `var(--vui-button-box-shadow, ${DEFAULT_BOX_SHADOW})` : void 0,
|
|
158
|
+
minW: icon ? 0 : `var(--vui-button-min-w, auto)`,
|
|
159
|
+
pointerEvents: hasState ? "none" : void 0,
|
|
160
|
+
w: isFullWidth ? "100%" : void 0
|
|
161
|
+
}),
|
|
162
|
+
buttonClassName: cs("vui-button", className, isActive && activeClassName, variantProp ? `vui-button-variant-${variantProp}` : void 0)
|
|
107
163
|
};
|
|
108
164
|
}, [
|
|
165
|
+
styles,
|
|
166
|
+
mergedProps,
|
|
167
|
+
variant,
|
|
109
168
|
borderWidthProp,
|
|
110
|
-
borderWidth,
|
|
111
|
-
spaceX,
|
|
112
169
|
icon,
|
|
113
170
|
iconLeft,
|
|
114
171
|
iconRight,
|
|
115
172
|
isLoading,
|
|
116
|
-
isSplit
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
const useDisabledProps = (disabled, styles) => {
|
|
120
|
-
return useMemo(() => disabled ? {
|
|
121
|
-
"aria-disabled": disabled,
|
|
122
|
-
disabledBg: styles?.container?.disabledBg ?? "bg",
|
|
123
|
-
disabledBorderColor: styles?.container?.disabledBorderColor ?? "borderColor",
|
|
124
|
-
disabledColor: styles?.container?.disabledColor ?? "color",
|
|
125
|
-
hoverBg: styles?.container?.disabledBg ?? "bg",
|
|
126
|
-
hoverBorderColor: styles?.container?.disabledBorderColor ?? "borderColor",
|
|
127
|
-
hoverColor: styles?.container?.disabledColor ?? "color",
|
|
128
|
-
activeBg: styles?.container?.disabledBg ?? "bg",
|
|
129
|
-
cursor: "not-allowed",
|
|
130
|
-
opacity: styles?.container?.disabledOpacity ?? DEFAULT_DISABLED_OPACITY
|
|
131
|
-
} : {}, [disabled, styles?.container]);
|
|
132
|
-
};
|
|
133
|
-
const useActiveProps = (isActive, styles) => {
|
|
134
|
-
return useMemo(() => isActive ? filterUndefined({
|
|
135
|
-
bg: styles?.container?.activeBg,
|
|
136
|
-
borderBg: styles?.container?.activeBorderBg,
|
|
137
|
-
color: styles?.container?.activeColor
|
|
138
|
-
}) : {}, [isActive, styles?.container]);
|
|
139
|
-
};
|
|
140
|
-
const useAliasedProps = (isRound, h, styles, isElevated, icon, hasState, isFullWidth) => {
|
|
141
|
-
return useMemo(() => filterUndefined({
|
|
142
|
-
borderRadius: isRound ? h / 2 : styles?.container?.borderRadius || 0,
|
|
143
|
-
boxShadow: isElevated ? DEFAULT_BOX_SHADOW : void 0,
|
|
144
|
-
minW: icon ? 0 : void 0,
|
|
145
|
-
pointerEvents: hasState ? "none" : void 0,
|
|
146
|
-
w: isFullWidth ? "100%" : void 0
|
|
147
|
-
}), [
|
|
173
|
+
isSplit,
|
|
174
|
+
disabled,
|
|
175
|
+
isActive,
|
|
148
176
|
isRound,
|
|
149
|
-
h,
|
|
150
|
-
styles?.container?.borderRadius,
|
|
151
177
|
isElevated,
|
|
152
|
-
icon,
|
|
153
178
|
hasState,
|
|
154
|
-
isFullWidth
|
|
155
|
-
]);
|
|
156
|
-
};
|
|
157
|
-
const useButtonClassName = (className, isActive, variantProp) => {
|
|
158
|
-
return useMemo(() => cs("vui-button", className, isActive && activeClassName, variantProp ? `vui-button-variant-${variantProp}` : void 0), [
|
|
179
|
+
isFullWidth,
|
|
159
180
|
className,
|
|
160
|
-
isActive,
|
|
161
181
|
variantProp
|
|
162
182
|
]);
|
|
163
183
|
};
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
184
|
+
/**
|
|
185
|
+
* Unified icon renderer for all button icon positions.
|
|
186
|
+
* Converts string icon names to ButtonIcon components with appropriate margins.
|
|
187
|
+
*
|
|
188
|
+
* @param iconProp - Icon name string or React element
|
|
189
|
+
* @param position - Icon position: 'main' (center), 'left', or 'right'
|
|
190
|
+
* @param isLoading - Whether to show loading spinner (only applies to left position)
|
|
191
|
+
* @returns Rendered icon or null
|
|
192
|
+
*/
|
|
193
|
+
const renderButtonIcon = (iconProp, position = "main", isLoading = false) => {
|
|
194
|
+
if (position === "left" && isLoading) return /* @__PURE__ */ jsx(ButtonIcon, {
|
|
195
|
+
...loadingIconProps,
|
|
196
|
+
ml: 0
|
|
197
|
+
});
|
|
198
|
+
if (!iconProp) return null;
|
|
199
|
+
if (!isString(iconProp)) return iconProp;
|
|
200
|
+
switch (position) {
|
|
201
|
+
case "left": return /* @__PURE__ */ jsx(ButtonIcon, {
|
|
202
|
+
name: iconProp,
|
|
203
|
+
ml: 0
|
|
204
|
+
});
|
|
205
|
+
case "right": return /* @__PURE__ */ jsx(ButtonIcon, {
|
|
206
|
+
name: iconProp,
|
|
207
|
+
mr: 0
|
|
208
|
+
});
|
|
209
|
+
default: return /* @__PURE__ */ jsx(ButtonIcon, { name: iconProp });
|
|
210
|
+
}
|
|
211
|
+
};
|
|
212
|
+
/**
|
|
213
|
+
* Renders the button content (text or children).
|
|
214
|
+
* Wraps text strings in ButtonText component with optional truncation.
|
|
215
|
+
*
|
|
216
|
+
* @param children - React children content
|
|
217
|
+
* @param text - Text string to render
|
|
218
|
+
* @param isTruncated - Whether to enable text truncation
|
|
219
|
+
* @returns Rendered content
|
|
220
|
+
*/
|
|
221
|
+
const renderContent = (children, text, isTruncated) => {
|
|
222
|
+
if (children) return children;
|
|
223
|
+
if (isReactText(text)) return /* @__PURE__ */ jsx(ButtonText, {
|
|
224
|
+
isTruncated,
|
|
225
|
+
text
|
|
226
|
+
});
|
|
227
|
+
return text;
|
|
192
228
|
};
|
|
193
229
|
/**
|
|
194
|
-
*
|
|
230
|
+
* Button Component
|
|
195
231
|
*
|
|
196
|
-
*
|
|
232
|
+
* A versatile button component that supports multiple states, sizes, variants, and behaviors.
|
|
233
|
+
* Part of the VUI design system with built-in accessibility features.
|
|
197
234
|
*
|
|
198
|
-
* Exposes some props to the children via context.
|
|
199
235
|
*/
|
|
200
236
|
const Button = vui((props, ref) => {
|
|
201
237
|
const mergedProps = {
|
|
@@ -211,63 +247,83 @@ const Button = vui((props, ref) => {
|
|
|
211
247
|
const stateData = useMemo(() => states[state] ?? {}, [states, state]);
|
|
212
248
|
const { iconProps: stateIconProps, ...stateProps } = stateData;
|
|
213
249
|
const iconRight = useMemo(() => isDropDown ? "falAngleDown" : iconRightProp, [isDropDown, iconRightProp]);
|
|
214
|
-
const
|
|
215
|
-
const
|
|
216
|
-
const
|
|
217
|
-
const disabledProps = useDisabledProps(disabled ?? false, styles);
|
|
218
|
-
const isActiveProps = useActiveProps(isActive ?? false, styles);
|
|
219
|
-
const aliasedProps = useAliasedProps(isRound ?? false, h, styles, isElevated ?? false, icon, hasState, isFullWidth ?? false);
|
|
220
|
-
const buttonClassName = useButtonClassName(className, isActive ?? false, variantProp);
|
|
221
|
-
const IconRenderer = useIconRenderer(icon, iconLeft, iconRight, isLoading ?? false, spacingProps, children, text, isTruncated ?? false);
|
|
250
|
+
const variantForTheme = useMemo(() => filterUndefined({ variant: stateData?.variant ?? variantProp }), [stateData?.variant, variantProp]);
|
|
251
|
+
const computed = useButtonComputed(mergedProps, variantForTheme, borderWidthProp, icon, iconLeft, iconRight, isLoading ?? false, isSplit ?? false, disabled ?? false, isActive ?? false, isRound ?? false, isElevated ?? false, hasState, isFullWidth ?? false, className, variantProp);
|
|
252
|
+
const hasTextContent = Boolean(children || text);
|
|
222
253
|
const contextValue = useMemo(() => filterUndefined({
|
|
223
254
|
disabled,
|
|
224
255
|
size,
|
|
225
|
-
variant:
|
|
256
|
+
variant: variantForTheme.variant,
|
|
257
|
+
isDropDown,
|
|
258
|
+
hasTextContent
|
|
226
259
|
}), [
|
|
227
260
|
disabled,
|
|
228
261
|
size,
|
|
229
|
-
|
|
262
|
+
variantForTheme.variant,
|
|
263
|
+
isDropDown,
|
|
264
|
+
hasTextContent
|
|
230
265
|
]);
|
|
231
266
|
const Content = useMemo(() => hasState ? ContentWrapper : Fragment, [hasState]);
|
|
232
|
-
const ariaLabel = useMemo(() => title || "", [title]);
|
|
233
267
|
const handleStateClick = useCallback((e) => {
|
|
234
268
|
e.stopPropagation();
|
|
235
269
|
}, []);
|
|
270
|
+
const finalVariant = stateData?.variant ?? variantProp;
|
|
271
|
+
const borderWidth = computed.spacing.borderWidth;
|
|
272
|
+
const plValue = computed.spacing.pl;
|
|
273
|
+
const prValue = computed.spacing.pr;
|
|
274
|
+
const hValue = computed.h;
|
|
275
|
+
const disabledPropsWithVars = useMemo(() => {
|
|
276
|
+
if (!computed.disabledProps || Object.keys(computed.disabledProps).length === 0) return {};
|
|
277
|
+
const { bg, borderColor, color, opacity, ...otherDisabledProps } = computed.disabledProps;
|
|
278
|
+
return {
|
|
279
|
+
...otherDisabledProps,
|
|
280
|
+
...bg !== void 0 && { bg: `var(--vui-button-disabled-bg, ${bg})` },
|
|
281
|
+
...borderColor !== void 0 && { borderColor: `var(--vui-button-disabled-border-color, ${borderColor})` },
|
|
282
|
+
...color !== void 0 && { color: `var(--vui-button-disabled-color, ${color})` },
|
|
283
|
+
...opacity !== void 0 && { opacity: `var(--vui-button-disabled-opacity, ${opacity})` }
|
|
284
|
+
};
|
|
285
|
+
}, [computed.disabledProps]);
|
|
286
|
+
const finalProps = useMemo(() => ({
|
|
287
|
+
borderWidth: `var(--vui-button-border-width, ${borderWidth}px)`,
|
|
288
|
+
className: computed.buttonClassName,
|
|
289
|
+
disabled,
|
|
290
|
+
focusRing: FOCUS_RING_SIZE,
|
|
291
|
+
h: `var(--vui-button-h, ${hValue}px)`,
|
|
292
|
+
pl: plValue,
|
|
293
|
+
pr: prValue,
|
|
294
|
+
ref,
|
|
295
|
+
title: title || "",
|
|
296
|
+
type: BUTTON_TYPE,
|
|
297
|
+
variant: finalVariant,
|
|
298
|
+
...computed.buttonStyles,
|
|
299
|
+
...computed.activeProps,
|
|
300
|
+
...disabledPropsWithVars,
|
|
301
|
+
...computed.aliasedProps,
|
|
302
|
+
...rest
|
|
303
|
+
}), [
|
|
304
|
+
borderWidth,
|
|
305
|
+
plValue,
|
|
306
|
+
prValue,
|
|
307
|
+
computed.buttonClassName,
|
|
308
|
+
computed.buttonStyles,
|
|
309
|
+
computed.activeProps,
|
|
310
|
+
disabledPropsWithVars,
|
|
311
|
+
computed.aliasedProps,
|
|
312
|
+
hValue,
|
|
313
|
+
disabled,
|
|
314
|
+
ref,
|
|
315
|
+
title,
|
|
316
|
+
finalVariant,
|
|
317
|
+
rest
|
|
318
|
+
]);
|
|
319
|
+
const mainIcon = renderButtonIcon(icon, "main");
|
|
320
|
+
const leftIcon = renderButtonIcon(iconLeft, "left", isLoading ?? false);
|
|
321
|
+
const rightIcon = renderButtonIcon(iconRight, "right");
|
|
322
|
+
const contentElement = renderContent(children, text, isTruncated ?? false);
|
|
236
323
|
return /* @__PURE__ */ jsx(ButtonProvider, {
|
|
237
324
|
value: contextValue,
|
|
238
325
|
children: /* @__PURE__ */ jsxs(ButtonBase, {
|
|
239
|
-
...
|
|
240
|
-
borderWidth: spacingProps.border,
|
|
241
|
-
className: buttonClassName,
|
|
242
|
-
disabled,
|
|
243
|
-
focusRing: FOCUS_RING_SIZE,
|
|
244
|
-
h,
|
|
245
|
-
pl: spacingProps.pl,
|
|
246
|
-
pr: spacingProps.pr,
|
|
247
|
-
ref,
|
|
248
|
-
title: title || ariaLabel,
|
|
249
|
-
type: BUTTON_TYPE,
|
|
250
|
-
variant,
|
|
251
|
-
...buttonStyles,
|
|
252
|
-
...isActiveProps,
|
|
253
|
-
...disabledProps,
|
|
254
|
-
...aliasedProps,
|
|
255
|
-
...rest
|
|
256
|
-
}), [
|
|
257
|
-
spacingProps,
|
|
258
|
-
buttonClassName,
|
|
259
|
-
disabled,
|
|
260
|
-
h,
|
|
261
|
-
ref,
|
|
262
|
-
title,
|
|
263
|
-
ariaLabel,
|
|
264
|
-
variant,
|
|
265
|
-
buttonStyles,
|
|
266
|
-
isActiveProps,
|
|
267
|
-
disabledProps,
|
|
268
|
-
aliasedProps,
|
|
269
|
-
rest
|
|
270
|
-
]),
|
|
326
|
+
...finalProps,
|
|
271
327
|
children: [hasState && /* @__PURE__ */ jsx(StateWrapper, {
|
|
272
328
|
onClick: handleStateClick,
|
|
273
329
|
...stateProps,
|
|
@@ -275,11 +331,15 @@ const Button = vui((props, ref) => {
|
|
|
275
331
|
m: "auto",
|
|
276
332
|
...stateIconProps
|
|
277
333
|
})
|
|
278
|
-
}), /* @__PURE__ */ jsxs(Content, { children: [
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
334
|
+
}), /* @__PURE__ */ jsxs(Content, { children: [
|
|
335
|
+
mainIcon,
|
|
336
|
+
!icon && /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
337
|
+
leftIcon,
|
|
338
|
+
contentElement,
|
|
339
|
+
rightIcon
|
|
340
|
+
] }),
|
|
341
|
+
icon && isDropDown && rightIcon
|
|
342
|
+
] })]
|
|
283
343
|
})
|
|
284
344
|
});
|
|
285
345
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"button.js","names":[],"sources":["../../src/button/button.tsx"],"sourcesContent":["import { Fragment, useMemo, useCallback } from 'react'\n\nimport { useButtonGroupContext } from '../buttonGroup/context'\nimport type { VuiComponent } from '../core'\nimport { styled, useStyleConfig, vui } from '../core'\nimport { activeClassName, cs, filterUndefined, isReactText, isString } from '../utils'\nimport type { ButtonProps } from './button.types'\nimport ButtonIcon from './buttonIcon'\nimport ButtonText from './buttonText'\nimport { buttonStateMapping, loadingIconProps } from './consts'\nimport { ButtonProvider } from './context'\n\n// Constants\nconst DEFAULT_BORDER_WIDTH = 1\nconst DEFAULT_BOX_SHADOW = 'md'\nconst DEFAULT_DISABLED_OPACITY = 0.5\nconst BUTTON_TYPE = 'button'\nconst FOCUS_RING_SIZE = 3\n\n// Memoized styled components\nconst ContentWrapper = styled.span`\n visibility: hidden;\n`\n\nconst StateWrapper = styled.span`\n bottom: 0;\n display: flex;\n left: 0;\n position: absolute;\n right: 0;\n top: 0;\n`\n\nexport const ButtonBase = styled.buttonBox<ButtonProps>`\n align-items: center;\n display: inline-flex;\n flex-shrink: 0;\n font-weight: medium;\n justify-content: center;\n line-height: normal;\n overflow: hidden;\n position: relative;\n transition-duration: fast;\n user-select: none;\n width: fit-content;\n\n &.vui-button-variant-menuLight.vui-button-variant-menuLight.vui-active:hover {\n background-color: #d3eef8;\n color: #003591;\n border-color-left: #003591;\n }\n &.vui-button-variant-menuDark.vui-button-variant-menuDark.vui-active:hover {\n background-color: #003591;\n color: #91ffba;\n border-color-left: #91ffba;\n }\n &.vui-button-variant-menuDark.vui-button-variant-menuDark:focus,\n &.vui-button-variant-menuDark.vui-button-variant-menuDark:focus-visible,\n &.vui-button-variant-menuLight.vui-button-variant-menuLight:focus,\n &.vui-button-variant-menuLight.vui-button-variant-menuLight:focus-visible {\n box-shadow: none;\n outline: none;\n border-bottom: 0 solid transparent;\n }\n &.vui-button-variant-menuDark.vui-button-variant-menuDark.focusAsBorder:focus,\n &.vui-button-variant-menuDark.vui-button-variant-menuDark.focusAsBorder:focus-visible,\n &.vui-button-variant-menuLight.vui-button-variant-menuLight.focusAsBorder:focus,\n &.vui-button-variant-menuLight.vui-button-variant-menuLight.focusAsBorder:focus-visible {\n transition: border-bottom 0s linear;\n border-bottom: 3px solid #d3eef8;\n }\n`\n\nconst useButtonStyles = (mergedProps: any, variant: any) => {\n const styles = useStyleConfig('Button', { ...mergedProps, ...variant }) || {}\n\n return useMemo(() => {\n const container = styles.container || {}\n const { borderWidth = DEFAULT_BORDER_WIDTH, h = 32, spaceX = 0, ...buttonStyles } = container\n return { styles, borderWidth, h, spaceX, buttonStyles }\n }, [mergedProps, variant])\n}\n\n// Utility function to extract numeric value from responsive or themed props\nconst extractNumericValue = (value: any): number => {\n if (typeof value === 'number') return value\n if (typeof value === 'string') {\n const parsed = parseInt(value, 10)\n return isNaN(parsed) ? DEFAULT_BORDER_WIDTH : parsed\n }\n return DEFAULT_BORDER_WIDTH\n}\n\nconst useSpacingProps = (\n borderWidthProp: any,\n borderWidth: number | undefined,\n spaceX: number,\n icon: any,\n iconLeft: any,\n iconRight: any,\n isLoading: boolean,\n isSplit: boolean,\n) => {\n return useMemo(() => {\n const border = extractNumericValue(borderWidthProp) || borderWidth || DEFAULT_BORDER_WIDTH\n const spaceXItem = spaceX - border\n const spaceXText = spaceX * 2 - border\n\n return {\n border,\n iconLeftMr: `${spaceXItem + 1}px`,\n iconRightMl: isSplit ? 'auto' : `${spaceXItem + 1}px`,\n pl: `${icon || iconLeft || isLoading ? spaceXItem : spaceXText}px`,\n pr: `${icon || iconRight ? spaceXItem : spaceXText}px`,\n }\n }, [borderWidthProp, borderWidth, spaceX, icon, iconLeft, iconRight, isLoading, isSplit])\n}\n\nconst useDisabledProps = (disabled: boolean, styles: any) => {\n return useMemo(\n () =>\n disabled\n ? {\n 'aria-disabled': disabled,\n disabledBg: styles?.container?.disabledBg ?? 'bg',\n disabledBorderColor: styles?.container?.disabledBorderColor ?? 'borderColor',\n disabledColor: styles?.container?.disabledColor ?? 'color',\n hoverBg: styles?.container?.disabledBg ?? 'bg',\n hoverBorderColor: styles?.container?.disabledBorderColor ?? 'borderColor',\n hoverColor: styles?.container?.disabledColor ?? 'color',\n activeBg: styles?.container?.disabledBg ?? 'bg',\n cursor: 'not-allowed',\n opacity: styles?.container?.disabledOpacity ?? DEFAULT_DISABLED_OPACITY,\n }\n : {},\n [disabled, styles?.container],\n )\n}\n\nconst useActiveProps = (isActive: boolean, styles: any) => {\n return useMemo(\n () =>\n isActive\n ? filterUndefined({\n bg: styles?.container?.activeBg,\n borderBg: styles?.container?.activeBorderBg,\n color: styles?.container?.activeColor,\n })\n : {},\n [isActive, styles?.container],\n )\n}\n\nconst useAliasedProps = (\n isRound: boolean,\n h: number,\n styles: any,\n isElevated: boolean,\n icon: any,\n hasState: boolean,\n isFullWidth: boolean,\n) => {\n return useMemo(\n () =>\n filterUndefined({\n borderRadius: isRound ? h / 2 : styles?.container?.borderRadius || 0,\n boxShadow: isElevated ? DEFAULT_BOX_SHADOW : undefined,\n minW: icon ? 0 : undefined,\n pointerEvents: hasState ? 'none' : undefined,\n w: isFullWidth ? '100%' : undefined,\n }),\n [isRound, h, styles?.container?.borderRadius, isElevated, icon, hasState, isFullWidth],\n )\n}\n\nconst useButtonClassName = (className: string | undefined, isActive: boolean, variantProp: string | undefined) => {\n return useMemo(\n () =>\n cs(\n 'vui-button',\n className,\n isActive && activeClassName,\n variantProp ? `vui-button-variant-${variantProp}` : undefined,\n ),\n [className, isActive, variantProp],\n )\n}\n\nconst useIconRenderer = (\n icon: any,\n iconLeft: any,\n iconRight: any,\n isLoading: boolean,\n spacingProps: any,\n children: any,\n text: any,\n isTruncated: boolean,\n) => {\n return useMemo(\n () => ({\n main: isString(icon) ? <ButtonIcon name={icon} /> : icon,\n left: isLoading ? (\n <ButtonIcon mr={spacingProps.iconLeftMr} {...loadingIconProps} />\n ) : isString(iconLeft) ? (\n <ButtonIcon mr={spacingProps.iconLeftMr} name={iconLeft} />\n ) : (\n iconLeft\n ),\n right: isString(iconRight) ? (\n <ButtonIcon ml={!children && !text ? 0 : spacingProps.iconRightMl} name={iconRight} />\n ) : (\n iconRight\n ),\n content: children ?? (isReactText(text) ? <ButtonText {...{ isTruncated, text }} /> : text),\n }),\n [icon, iconLeft, iconRight, isLoading, spacingProps, children, text, isTruncated],\n )\n}\n\n/**\n * Triggers given actions on click. Supports multiple states, sizes and variants.\n *\n * Comes with additional props for extra behavior, like loading, disabled or elevated.\n *\n * Exposes some props to the children via context.\n */\nexport const Button = vui<'button', ButtonProps>((props, ref) => {\n const buttonGroupProps = useButtonGroupContext()\n const mergedProps = { ...buttonGroupProps, ...props }\n const {\n borderWidth: borderWidthProp,\n children,\n className,\n colorScheme,\n disabled,\n dropDownIcon = 'falAngleDown',\n icon,\n iconLeft,\n iconRight: iconRightProp,\n isActive,\n isDropDown,\n isElevated,\n isFullWidth,\n isLoading,\n isRound,\n isSplit,\n isTruncated,\n size,\n state = '',\n stateMapping,\n text,\n title,\n variant: variantProp,\n ...rest\n } = mergedProps\n\n // Memoized calculations\n const hasState = useMemo(() => Boolean(state) && state !== 'idle', [state])\n const states = useMemo(() => ({ ...buttonStateMapping, ...stateMapping }), [stateMapping])\n const stateData = useMemo(() => states[state] ?? {}, [states, state])\n const { iconProps: stateIconProps, ...stateProps } = stateData\n\n const iconRight = useMemo(() => (isDropDown ? 'falAngleDown' : iconRightProp), [isDropDown, iconRightProp])\n\n const variant = useMemo(\n () =>\n filterUndefined({\n variant: stateData?.variant ?? variantProp,\n }),\n [stateData?.variant, variantProp],\n )\n\n // Custom hooks for optimization\n const { styles, borderWidth, h, spaceX, buttonStyles } = useButtonStyles(mergedProps, variant)\n const spacingProps = useSpacingProps(\n borderWidthProp,\n borderWidth,\n spaceX,\n icon,\n iconLeft,\n iconRight,\n isLoading ?? false,\n isSplit ?? false,\n )\n const disabledProps = useDisabledProps(disabled ?? false, styles)\n const isActiveProps = useActiveProps(isActive ?? false, styles)\n const aliasedProps = useAliasedProps(\n isRound ?? false,\n h,\n styles,\n isElevated ?? false,\n icon,\n hasState,\n isFullWidth ?? false,\n )\n const buttonClassName = useButtonClassName(className, isActive ?? false, variantProp)\n const IconRenderer = useIconRenderer(\n icon,\n iconLeft,\n iconRight,\n isLoading ?? false,\n spacingProps,\n children,\n text,\n isTruncated ?? false,\n )\n\n // Memoized context value\n const contextValue = useMemo(\n () => filterUndefined({ disabled, size, variant: variant.variant }),\n [disabled, size, variant.variant],\n )\n\n // Memoized content wrapper\n const Content = useMemo(() => (hasState ? ContentWrapper : Fragment), [hasState])\n\n // Memoized aria label\n const ariaLabel = useMemo(() => title || '', [title])\n\n // Optimized event handler\n const handleStateClick = useCallback((e: { stopPropagation: () => void }) => {\n e.stopPropagation()\n }, [])\n\n // Memoized final props\n const finalProps = useMemo(\n () => ({\n borderWidth: spacingProps.border,\n className: buttonClassName,\n disabled,\n focusRing: FOCUS_RING_SIZE,\n h,\n pl: spacingProps.pl,\n pr: spacingProps.pr,\n ref,\n title: title || ariaLabel,\n type: BUTTON_TYPE,\n variant,\n ...buttonStyles,\n ...isActiveProps,\n ...disabledProps,\n ...aliasedProps,\n ...rest,\n }),\n [\n spacingProps,\n buttonClassName,\n disabled,\n h,\n ref,\n title,\n ariaLabel,\n variant,\n buttonStyles,\n isActiveProps,\n disabledProps,\n aliasedProps,\n rest,\n ],\n )\n\n return (\n <ButtonProvider value={contextValue}>\n <ButtonBase {...finalProps}>\n {hasState && (\n <StateWrapper onClick={handleStateClick} {...stateProps}>\n <ButtonIcon m=\"auto\" {...stateIconProps} />\n </StateWrapper>\n )}\n\n <Content>\n {IconRenderer.main}\n {!icon && (\n <>\n {IconRenderer.left}\n {IconRenderer.content}\n {IconRenderer.right}\n </>\n )}\n </Content>\n </ButtonBase>\n </ButtonProvider>\n )\n}) as VuiComponent<'button', ButtonProps> & {\n Icon: typeof ButtonIcon\n Text: typeof ButtonText\n}\n\nButton.Icon = ButtonIcon\nButton.Text = ButtonText\nButton.displayName = 'Button'\n\nexport default Button\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"button.js","names":[],"sources":["../../src/button/button.tsx"],"sourcesContent":["import { Fragment, useMemo, useCallback } from 'react'\n\nimport { useButtonGroupContext } from '../buttonGroup/context'\nimport type { VuiComponent } from '../core'\nimport { styled, useStyleConfig, vui } from '../core'\nimport type { SystemProps } from '../system'\nimport { activeClassName, cs, filterUndefined, isReactText, isString } from '../utils'\nimport type { ButtonProps } from './button.types'\nimport ButtonIcon from './buttonIcon'\nimport ButtonText from './buttonText'\nimport { buttonStateMapping, loadingIconProps } from './consts'\nimport { ButtonProvider } from './context'\n\n// Types for internal use\ninterface ButtonStyleContainer {\n borderWidth?: number\n h?: number\n spaceX?: number\n disabledBg?: string\n disabledBorderColor?: string\n disabledColor?: string\n disabledOpacity?: number\n activeBg?: string\n activeBorderBg?: string\n activeColor?: string\n borderRadius?: number\n [key: string]: any\n}\n\ninterface ButtonComputedStyles {\n borderColor: string\n activeBorderColor: string\n activeColor: string\n hoverBorderColor: string\n hoverColor: string\n [key: string]: any\n}\n\ninterface SpacingProps {\n borderWidth: number\n pl: string\n pr: string\n}\n\ninterface ComputedResult {\n styles: { container?: ButtonStyleContainer; [key: string]: any }\n borderWidth: number\n h: number\n spaceX: number\n buttonStyles: ButtonComputedStyles\n spacing: SpacingProps\n disabledProps: SystemProps\n activeProps: SystemProps\n aliasedProps: SystemProps\n buttonClassName: string\n}\n\n// Constants - TODO: Move to theme tokens for better maintainability\nconst DEFAULT_BORDER_WIDTH = 1\nconst DEFAULT_BOX_SHADOW = 'md'\nconst DEFAULT_DISABLED_OPACITY = 0.5\nconst BUTTON_TYPE = 'button'\nconst FOCUS_RING_SIZE = 3\nconst MENU_FOCUS_BORDER_WIDTH = 3 // Width of focus border for menu variants\n\n// Memoized styled components\nconst ContentWrapper = styled.span`\n visibility: hidden;\n`\n\n// StateWrapper overlays the button content during state transitions (loading, success, error)\n// Positioned absolutely to avoid layout shifts and uses pointer-events: none on parent\nconst StateWrapper = styled.span`\n bottom: 0;\n display: flex;\n left: 0;\n position: absolute;\n right: 0;\n top: 0;\n`\n\nexport const ButtonBase = styled.buttonBox<ButtonProps>`\n align-items: center;\n display: inline-flex;\n flex-shrink: 0;\n font-weight: var(--vui-button-font-weight, medium);\n justify-content: center;\n line-height: normal;\n overflow: hidden;\n position: relative;\n transition-duration: var(--vui-button-transition-duration, fast);\n user-select: none;\n width: fit-content;\n\n /* Menu variant focus states - default removes focus ring\n * Used when focusAsBorder class is NOT present (standard focus ring behavior)\n */\n &.vui-button-variant-menuDark:not(.focusAsBorder):focus,\n &.vui-button-variant-menuDark:not(.focusAsBorder):focus-visible,\n &.vui-button-variant-menuLight:not(.focusAsBorder):focus,\n &.vui-button-variant-menuLight:not(.focusAsBorder):focus-visible {\n box-shadow: none;\n outline: none;\n }\n\n /* Menu variant focus states - with border indicator for accessibility\n * Used when focusAsBorder class IS present (alternative focus indication via border)\n * Color: theme menuLight/menuDark.focusBorderBottomColor\n */\n &.vui-button-variant-menuDark.focusAsBorder:focus,\n &.vui-button-variant-menuDark.focusAsBorder:focus-visible,\n &.vui-button-variant-menuLight.focusAsBorder:focus,\n &.vui-button-variant-menuLight.focusAsBorder:focus-visible {\n transition: border-bottom 0s linear;\n border-bottom: ${MENU_FOCUS_BORDER_WIDTH}px solid var(--color-sky-blue-90); /* theme: focusBorderBottomColor */\n }\n`\n\n/**\n * Extracts a numeric value from various input types (number, string, etc.)\n * @param value - Value to extract number from\n * @param fallback - Default value if extraction fails\n * @returns Extracted number or fallback\n */\nconst extractNumericValue = (value: any, fallback: number = DEFAULT_BORDER_WIDTH): number => {\n if (typeof value === 'number') return value\n if (typeof value === 'string') {\n const parsed = parseInt(value, 10)\n if (!isNaN(parsed)) return parsed\n }\n return fallback\n}\n\n/**\n * Consolidated hook for computing all button styles and props.\n * Combines theme resolution, spacing, state-based styles, and className generation.\n * Reduces memoization overhead by computing all derived values in a single pass.\n *\n * @param mergedProps - Combined props from ButtonGroup context and component props\n * @param variant - Resolved variant object with theme overrides\n * @param borderWidthProp - Optional border width override from props\n * @param icon - Main icon (replaces all content when present)\n * @param iconLeft - Left icon slot\n * @param iconRight - Right icon slot\n * @param isLoading - Loading state flag\n * @param isSplit - Deprecated: Split layout mode\n * @param disabled - Disabled state flag\n * @param isActive - Active state flag\n * @param isRound - Round/pill-shaped borders flag\n * @param isElevated - Box shadow elevation flag\n * @param hasState - Whether button has a state overlay\n * @param isFullWidth - Full width layout flag\n * @param className - Additional CSS classes\n * @param variantProp - Variant name for class generation\n * @returns Computed styles, spacing, props, and className\n */\nconst useButtonComputed = (\n mergedProps: ButtonProps & SystemProps,\n variant: { variant?: string },\n borderWidthProp: any, // Keep as any due to complex BorderWidthProp type from xstyled\n icon: any,\n iconLeft: any,\n iconRight: any,\n isLoading: boolean,\n isSplit: boolean,\n disabled: boolean,\n isActive: boolean,\n isRound: boolean,\n isElevated: boolean,\n hasState: boolean,\n isFullWidth: boolean,\n className: string | undefined,\n variantProp: string | undefined,\n): ComputedResult => {\n const styles = useStyleConfig('Button', { ...mergedProps, ...variant }) || {}\n\n return useMemo(() => {\n // Theme and style resolution\n const container = styles.container || {}\n const { borderWidth = DEFAULT_BORDER_WIDTH, h = 32, spaceX = 0, ...buttonStyles } = container\n\n // Normalize button styles with fallbacks for text colors only\n const normalizedStyles = {\n ...buttonStyles,\n activeColor: buttonStyles.activeColor ?? buttonStyles.color,\n hoverColor: buttonStyles.hoverColor ?? buttonStyles.color,\n }\n\n // Spacing calculations\n // borderWidth value used for spacing calculations (affects padding adjustments)\n const border = extractNumericValue(borderWidthProp) || borderWidth || DEFAULT_BORDER_WIDTH\n const spaceXItem = spaceX - border\n const spaceXText = spaceX * 2 - border\n\n const spacing = {\n borderWidth: border, // Store borderWidth for use in finalProps\n pl: `var(--vui-button-px-start, ${icon || iconLeft || isLoading ? spaceXItem : spaceXText}px)`,\n pr: `var(--vui-button-px-end, ${icon || iconRight ? spaceXItem : spaceXText}px)`,\n }\n\n // Disabled state props with CSS variable overrides\n // Theme tokens (like landGreen.70) are resolved by styled system, then wrapped in CSS variables\n // This allows both theme resolution and CSS variable overrides to work together\n const disabledProps = disabled\n ? filterUndefined({\n 'aria-disabled': disabled,\n bg: container?.disabledBg ?? undefined,\n borderColor: container?.disabledBorderColor ?? undefined,\n color: container?.disabledColor ?? undefined,\n hoverBg: container?.disabledBg ?? normalizedStyles.bg,\n hoverBorderColor: container?.disabledBorderColor ?? normalizedStyles.borderColor,\n hoverColor: container?.disabledColor ?? normalizedStyles.color,\n activeBg: container?.disabledBg ?? normalizedStyles.bg,\n cursor: 'not-allowed',\n opacity: container?.disabledOpacity ?? DEFAULT_DISABLED_OPACITY,\n })\n : {}\n\n // Active state props - prevents hover effects when button is active\n // Active state (also called \"loading\" in VUI Next) should not be interactive\n const activeProps = isActive\n ? filterUndefined({\n bg: container?.activeBg,\n borderColor: container?.activeBorderColor,\n color: container?.activeColor,\n hoverBg: container?.activeBg,\n hoverBorderColor: container?.activeBorderColor,\n hoverColor: container?.activeColor,\n cursor: 'default',\n })\n : {}\n\n // Aliased props (computed properties)\n const aliasedProps = filterUndefined({\n borderRadius: isRound ? h / 2 : `var(--vui-button-border-radius, ${container?.borderRadius || 0}px)`,\n boxShadow: isElevated ? `var(--vui-button-box-shadow, ${DEFAULT_BOX_SHADOW})` : undefined,\n minW: icon ? 0 : `var(--vui-button-min-w, auto)`,\n pointerEvents: hasState ? 'none' : undefined,\n w: isFullWidth ? '100%' : undefined,\n })\n\n // Class name generation\n const buttonClassName = cs(\n 'vui-button',\n className,\n isActive && activeClassName,\n variantProp ? `vui-button-variant-${variantProp}` : undefined,\n )\n\n return {\n styles,\n borderWidth,\n h,\n spaceX,\n buttonStyles: normalizedStyles,\n spacing,\n disabledProps,\n activeProps,\n aliasedProps,\n buttonClassName,\n }\n }, [\n styles,\n mergedProps,\n variant,\n borderWidthProp,\n icon,\n iconLeft,\n iconRight,\n isLoading,\n isSplit,\n disabled,\n isActive,\n isRound,\n isElevated,\n hasState,\n isFullWidth,\n className,\n variantProp,\n ])\n}\n\n/**\n * Unified icon renderer for all button icon positions.\n * Converts string icon names to ButtonIcon components with appropriate margins.\n *\n * @param iconProp - Icon name string or React element\n * @param position - Icon position: 'main' (center), 'left', or 'right'\n * @param isLoading - Whether to show loading spinner (only applies to left position)\n * @returns Rendered icon or null\n */\nconst renderButtonIcon = (\n iconProp: any,\n position: 'main' | 'left' | 'right' = 'main',\n isLoading: boolean = false,\n): React.ReactElement | null => {\n // Handle loading state for left icon\n if (position === 'left' && isLoading) {\n return <ButtonIcon {...loadingIconProps} ml={0} />\n }\n\n if (!iconProp) return null\n\n // Return existing React elements as-is\n if (!isString(iconProp)) return iconProp\n\n // Apply position-specific margins for string icon names\n switch (position) {\n case 'left':\n return <ButtonIcon name={iconProp} ml={0} />\n case 'right':\n return <ButtonIcon name={iconProp} mr={0} />\n case 'main':\n default:\n return <ButtonIcon name={iconProp} />\n }\n}\n\n/**\n * Renders the button content (text or children).\n * Wraps text strings in ButtonText component with optional truncation.\n *\n * @param children - React children content\n * @param text - Text string to render\n * @param isTruncated - Whether to enable text truncation\n * @returns Rendered content\n */\nconst renderContent = (children: any, text: any, isTruncated: boolean): React.ReactNode => {\n if (children) return children\n if (isReactText(text)) return <ButtonText isTruncated={isTruncated} text={text} />\n return text\n}\n\n/**\n * Button Component\n *\n * A versatile button component that supports multiple states, sizes, variants, and behaviors.\n * Part of the VUI design system with built-in accessibility features.\n *\n */\nexport const Button = vui<'button', ButtonProps>((props, ref) => {\n const buttonGroupProps = useButtonGroupContext()\n const mergedProps = { ...buttonGroupProps, ...props }\n const {\n borderWidth: borderWidthProp,\n children,\n className,\n colorScheme,\n disabled,\n dropDownIcon = 'falAngleDown',\n icon,\n iconLeft,\n iconRight: iconRightProp,\n isActive,\n isDropDown,\n isElevated,\n isFullWidth,\n isLoading,\n isRound,\n isSplit,\n isTruncated,\n size,\n state = '',\n stateMapping,\n text,\n title,\n variant: variantProp,\n ...rest\n } = mergedProps\n\n // Memoized calculations\n const hasState = useMemo(() => Boolean(state) && state !== 'idle', [state])\n const states = useMemo(() => ({ ...buttonStateMapping, ...stateMapping }), [stateMapping])\n const stateData = useMemo(() => states[state] ?? {}, [states, state])\n const { iconProps: stateIconProps, ...stateProps } = stateData\n\n const iconRight = useMemo(() => (isDropDown ? 'falAngleDown' : iconRightProp), [isDropDown, iconRightProp])\n\n const variantForTheme = useMemo(\n () =>\n filterUndefined({\n variant: stateData?.variant ?? variantProp,\n }),\n [stateData?.variant, variantProp],\n )\n\n // Consolidated computation of all button styles and props\n const computed = useButtonComputed(\n mergedProps,\n variantForTheme,\n borderWidthProp,\n icon,\n iconLeft,\n iconRight,\n isLoading ?? false,\n isSplit ?? false,\n disabled ?? false,\n isActive ?? false,\n isRound ?? false,\n isElevated ?? false,\n hasState,\n isFullWidth ?? false,\n className,\n variantProp,\n )\n\n // Memoized context value\n const hasTextContent = Boolean(children || text)\n const contextValue = useMemo(\n () => filterUndefined({ disabled, size, variant: variantForTheme.variant, isDropDown, hasTextContent }),\n [disabled, size, variantForTheme.variant, isDropDown, hasTextContent],\n )\n\n // Memoized content wrapper\n const Content = useMemo(() => (hasState ? ContentWrapper : Fragment), [hasState])\n\n // Optimized event handler - prevents event bubbling during state overlay interactions\n // This ensures clicks on the state overlay (loading/success/error) don't trigger button actions\n const handleStateClick = useCallback((e: { stopPropagation: () => void }) => {\n e.stopPropagation()\n }, [])\n\n // Memoized final props - using primitives to avoid object reference changes\n const finalVariant = (stateData?.variant ?? variantProp) as string | undefined\n\n // Extract primitive values for stable dependencies\n const borderWidth = computed.spacing.borderWidth\n const plValue = computed.spacing.pl\n const prValue = computed.spacing.pr\n const hValue = computed.h\n\n // Wrap disabled props with CSS variables for customization\n const disabledPropsWithVars = useMemo(() => {\n if (!computed.disabledProps || Object.keys(computed.disabledProps).length === 0) return {}\n\n const { bg, borderColor, color, opacity, ...otherDisabledProps } = computed.disabledProps\n\n return {\n ...otherDisabledProps,\n ...(bg !== undefined && { bg: `var(--vui-button-disabled-bg, ${bg})` }),\n ...(borderColor !== undefined && { borderColor: `var(--vui-button-disabled-border-color, ${borderColor})` }),\n ...(color !== undefined && { color: `var(--vui-button-disabled-color, ${color})` }),\n ...(opacity !== undefined && { opacity: `var(--vui-button-disabled-opacity, ${opacity})` }),\n }\n }, [computed.disabledProps])\n\n const finalProps = useMemo(\n () => ({\n borderWidth: `var(--vui-button-border-width, ${borderWidth}px)`,\n className: computed.buttonClassName,\n disabled,\n focusRing: FOCUS_RING_SIZE,\n h: `var(--vui-button-h, ${hValue}px)`,\n pl: plValue,\n pr: prValue,\n ref,\n title: title || '',\n type: BUTTON_TYPE as 'button',\n variant: finalVariant,\n ...computed.buttonStyles,\n ...computed.activeProps,\n ...disabledPropsWithVars,\n ...computed.aliasedProps,\n ...rest,\n }),\n [\n borderWidth,\n plValue,\n prValue,\n computed.buttonClassName,\n computed.buttonStyles,\n computed.activeProps,\n disabledPropsWithVars,\n computed.aliasedProps,\n hValue,\n disabled,\n ref,\n title,\n finalVariant,\n rest,\n ],\n )\n\n // Render icon elements (not memoized to avoid stale references)\n const mainIcon = renderButtonIcon(icon, 'main')\n const leftIcon = renderButtonIcon(iconLeft, 'left', isLoading ?? false)\n const rightIcon = renderButtonIcon(iconRight, 'right')\n const contentElement = renderContent(children, text, isTruncated ?? false)\n\n return (\n <ButtonProvider value={contextValue}>\n <ButtonBase {...finalProps}>\n {hasState && (\n <StateWrapper onClick={handleStateClick} {...stateProps}>\n <ButtonIcon m=\"auto\" {...stateIconProps} />\n </StateWrapper>\n )}\n\n <Content>\n {mainIcon}\n {!icon && (\n <>\n {leftIcon}\n {contentElement}\n {rightIcon}\n </>\n )}\n {icon && isDropDown && rightIcon}\n </Content>\n </ButtonBase>\n </ButtonProvider>\n )\n}) as VuiComponent<'button', ButtonProps> & {\n Icon: typeof ButtonIcon\n Text: typeof ButtonText\n}\n\nButton.Icon = ButtonIcon\nButton.Text = ButtonText\nButton.displayName = 'Button'\n\nexport default Button\n"],"mappings":"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"}
|
|
@@ -9,14 +9,23 @@ import { jsx } from "react/jsx-runtime";
|
|
|
9
9
|
const isConceptualIcon = (name) => name?.startsWith("con");
|
|
10
10
|
/** Displays an icon within the Button. */
|
|
11
11
|
const ButtonIcon = vui((props, ref) => {
|
|
12
|
-
const { className, name, ...rest } = props;
|
|
13
|
-
const
|
|
12
|
+
const { className, name, mr, ml, ...rest } = props;
|
|
13
|
+
const buttonContext = useButtonContext();
|
|
14
|
+
const styles = useStyleConfig("Button", buttonContext);
|
|
14
15
|
const verifiedName = isConceptualIcon(name) ? void 0 : name;
|
|
16
|
+
const isIconOnly = !buttonContext?.hasTextContent;
|
|
17
|
+
const defaultMarginRight = isIconOnly ? 0 : styles.icon?.mr || 0;
|
|
18
|
+
const defaultMarginLeft = isIconOnly ? 0 : styles.icon?.ml || 0;
|
|
19
|
+
const marginRight = mr !== void 0 ? mr : `var(--vui-button-icon-mr, ${defaultMarginRight})`;
|
|
20
|
+
const marginLeft = ml !== void 0 ? ml : `var(--vui-button-icon-ml, ${defaultMarginLeft})`;
|
|
15
21
|
return /* @__PURE__ */ jsx(Icon, {
|
|
16
22
|
className: cs("vui-buttonIcon", className),
|
|
23
|
+
...styles.icon,
|
|
24
|
+
fontSize: `var(--vui-button-icon-size, ${styles.icon?.fontSize || "1rem"})`,
|
|
25
|
+
ml: marginLeft,
|
|
26
|
+
mr: marginRight,
|
|
17
27
|
name: verifiedName,
|
|
18
28
|
ref,
|
|
19
|
-
...styles.icon,
|
|
20
29
|
...rest
|
|
21
30
|
});
|
|
22
31
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"buttonIcon.js","names":[],"sources":["../../src/button/buttonIcon.tsx"],"sourcesContent":["import { useStyleConfig, vui } from '../core'\nimport type { IconProp, IconProps } from '../icon'\nimport Icon from '../icon'\nimport { cs } from '../utils'\nimport { useButtonContext } from './context'\n\nconst isConceptualIcon = (name?: IconProp) => name?.startsWith('con')\n\n/** Displays an icon within the Button. */\nexport const ButtonIcon = vui<'svg', IconProps>((props, ref) => {\n const { className, name, ...rest } = props\n const styles = useStyleConfig('Button',
|
|
1
|
+
{"version":3,"file":"buttonIcon.js","names":[],"sources":["../../src/button/buttonIcon.tsx"],"sourcesContent":["import { useStyleConfig, vui } from '../core'\nimport type { IconProp, IconProps } from '../icon'\nimport Icon from '../icon'\nimport { cs } from '../utils'\nimport { useButtonContext } from './context'\n\nconst isConceptualIcon = (name?: IconProp) => name?.startsWith('con')\n\n/** Displays an icon within the Button. */\nexport const ButtonIcon = vui<'svg', IconProps>((props, ref) => {\n const { className, name, mr, ml, ...rest } = props\n const buttonContext = useButtonContext()\n const styles = useStyleConfig('Button', buttonContext)\n const verifiedName = isConceptualIcon(name) ? undefined : name\n\n // For icon-only buttons (no text), remove margins\n // This applies to both icon-only dropdown buttons and regular icon-only buttons\n const isIconOnly = !buttonContext?.hasTextContent\n const defaultMarginRight = isIconOnly ? 0 : styles.icon?.mr || 0\n const defaultMarginLeft = isIconOnly ? 0 : styles.icon?.ml || 0\n\n // Apply CSS variable overrides for icon spacing\n const marginRight = mr !== undefined ? mr : `var(--vui-button-icon-mr, ${defaultMarginRight})`\n const marginLeft = ml !== undefined ? ml : `var(--vui-button-icon-ml, ${defaultMarginLeft})`\n\n return (\n <Icon\n className={cs('vui-buttonIcon', className)}\n {...styles.icon}\n fontSize={`var(--vui-button-icon-size, ${styles.icon?.fontSize || '1rem'})`}\n ml={marginLeft}\n mr={marginRight}\n name={verifiedName}\n ref={ref}\n {...rest}\n />\n )\n})\n\nButtonIcon.displayName = 'ButtonIcon'\nexport default ButtonIcon\n"],"mappings":";;;;;;;;AAMA,MAAM,oBAAoB,SAAoB,MAAM,WAAW,MAAM;;AAGrE,MAAa,aAAa,KAAuB,OAAO,QAAQ;CAC9D,MAAM,EAAE,WAAW,MAAM,IAAI,IAAI,GAAG,SAAS;CAC7C,MAAM,gBAAgB,kBAAkB;CACxC,MAAM,SAAS,eAAe,UAAU,cAAc;CACtD,MAAM,eAAe,iBAAiB,KAAK,GAAG,SAAY;CAI1D,MAAM,aAAa,CAAC,eAAe;CACnC,MAAM,qBAAqB,aAAa,IAAI,OAAO,MAAM,MAAM;CAC/D,MAAM,oBAAoB,aAAa,IAAI,OAAO,MAAM,MAAM;CAG9D,MAAM,cAAc,OAAO,SAAY,KAAK,6BAA6B,mBAAmB;CAC5F,MAAM,aAAa,OAAO,SAAY,KAAK,6BAA6B,kBAAkB;AAE1F,QACE,oBAAC;EACC,WAAW,GAAG,kBAAkB,UAAU;EAC1C,GAAI,OAAO;EACX,UAAU,+BAA+B,OAAO,MAAM,YAAY,OAAO;EACzE,IAAI;EACJ,IAAI;EACJ,MAAM;EACD;EACL,GAAI;GACJ;EAEJ;AAEF,WAAW,cAAc"}
|