@veracity/vui
Version:
Veracity UI is a React component library crafted for use within Veracity applications and pages. Based on Styled Components and @xstyled.
193 lines (191 loc) • 6.63 kB
JavaScript
import { filterUndefined } from "../utils/object.js";
import styled from "../core/styled.js";
import vui from "../core/vui.js";
import { useButtonGroupContext } from "../buttonGroup/context.js";
import { ButtonProvider } from "./context.js";
import { ButtonIcon } from "./buttonIcon.js";
import { ButtonText } from "./buttonText.js";
import { CSS_VAR_MAPPINGS } from "./consts.js";
import { renderButtonIcon, renderContent } from "./buttonHelpers.js";
import { resolveVariantKey, wrapWithVars } from "./utils.js";
import { useButtonComputed } from "./useButtonComputed.js";
import { useEffect, useMemo } from "react";
import { jsx, jsxs } from "react/jsx-runtime";
//#region src/button/button.tsx
const BUTTON_TYPE = "button";
const FOCUS_RING_SIZE = 3;
const ButtonBase = styled.buttonBox`
align-items: center;
display: inline-flex;
flex-shrink: 0;
font-weight: var(--vui-button-font-weight, 500);
justify-content: center;
line-height: 1.2;
overflow: hidden;
position: relative;
transition-duration: var(--vui-button-transition-duration, fast);
user-select: none;
width: fit-content;
`;
/**
* Button Component
*
* A versatile button component that supports multiple states, sizes, variants, and behaviors.
* Part of the VUI design system with built-in accessibility features.
*
*/
const Button = vui((props, ref) => {
const { borderWidth: borderWidthProp, children, className, colorScheme, disabled, dropDownIcon = "uiAngleDown", intent: intentProp, isSplit: _isSplit, icon, startIcon, iconLeft, endIcon: endIconProp, iconRight, isActive = false, isDropDown, isElevated = false, isFullWidth = false, isLoading = false, loadingText = "Loading", isRound = false, isTruncated = false, size, text, title, variant: variantProp, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledby, onClick, ...rest } = {
...useButtonGroupContext(),
...props
};
const hasTextContent = Boolean(children || text);
const isIconOnly = !children && !text && !isDropDown;
const resolvedStartIcon = startIcon ?? iconLeft;
const resolvedEndIconProp = endIconProp ?? iconRight;
const hasStartIcon = Boolean(resolvedStartIcon || icon || isLoading);
const hasEndIcon = Boolean(resolvedEndIconProp || isDropDown);
const endIcon = isDropDown ? dropDownIcon : resolvedEndIconProp;
const variantForTheme = useMemo(() => filterUndefined({ variant: resolveVariantKey(variantProp, intentProp) }), [variantProp, intentProp]);
const computed = useButtonComputed({
size,
variant: variantForTheme,
intentProp,
borderWidthProp,
disabled: disabled ?? false,
isActive,
isIconOnly,
isRound,
isElevated,
hasStartIcon,
hasEndIcon,
isFullWidth,
className,
variantProp
});
const contextValue = useMemo(() => filterUndefined({
disabled,
size,
variant: variantForTheme.variant,
intent: intentProp,
isDropDown,
hasTextContent,
isLoading
}), [
disabled,
size,
variantForTheme.variant,
intentProp,
isDropDown,
hasTextContent,
isLoading
]);
const finalVariant = variantForTheme.variant;
const borderWidth = computed.spacing.borderWidth;
const plValue = computed.spacing.pl;
const prValue = computed.spacing.pr;
const ptValue = computed.spacing.pt;
const pbValue = computed.spacing.pb;
const gapValue = computed.spacing.gap;
const propsWithVars = useMemo(() => ({
...wrapWithVars(computed.buttonStyles, CSS_VAR_MAPPINGS.base),
...wrapWithVars(computed.activeProps, CSS_VAR_MAPPINGS.active),
...wrapWithVars(computed.disabledProps, CSS_VAR_MAPPINGS.disabled)
}), [
computed.buttonStyles,
computed.activeProps,
computed.disabledProps
]);
const finalProps = {
...useMemo(() => ({
borderWidth: `var(--vui-button-border-width, ${borderWidth}px)`,
className: computed.buttonClassName,
disabled,
"aria-label": isLoading && isIconOnly ? loadingText || "Loading" : ariaLabel,
"aria-labelledby": ariaLabelledby,
"aria-busy": isLoading ? true : void 0,
"aria-disabled": isLoading ? true : void 0,
"aria-haspopup": isDropDown ? true : void 0,
focusRing: FOCUS_RING_SIZE,
gap: gapValue,
pl: plValue,
pr: prValue,
pt: ptValue,
pb: pbValue,
onClick: isLoading ? void 0 : onClick,
pointerEvents: isLoading ? "none" : void 0,
ref,
title: title || void 0,
type: BUTTON_TYPE,
variant: finalVariant,
...propsWithVars,
...computed.aliasedProps
}), [
borderWidth,
plValue,
prValue,
ptValue,
pbValue,
gapValue,
computed.buttonClassName,
computed.aliasedProps,
disabled,
ariaLabel,
ariaLabelledby,
isDropDown,
ref,
title,
isLoading,
onClick,
finalVariant,
isIconOnly,
loadingText,
propsWithVars
]),
...rest
};
useEffect(() => {
if (_isSplit !== void 0) console.warn("[Button] The `isSplit` prop is deprecated and has no effect. It will be removed in VUI 5.x.");
if (colorScheme !== void 0) console.warn("[Button] `colorScheme` is deprecated. Use `variant` + `intent` instead.");
if (iconLeft !== void 0) console.warn("[Button] `iconLeft` is deprecated. Use `startIcon` instead.");
if (iconRight !== void 0) console.warn("[Button] `iconRight` is deprecated. Use `endIcon` instead.");
if (variantProp !== void 0 && ![
"primary",
"secondary",
"tertiary"
].includes(variantProp) && /^(primary|secondary|tertiary)(Brand|Contrast|Success|Danger|Warning)$/.test(variantProp)) console.warn(`[Button] Combined variant "${variantProp}" is deprecated. Use separate \`variant\` + \`intent\` props instead (e.g. variant="primary" intent="brand").`);
if (!children && !text && !Boolean(ariaLabel || ariaLabelledby || title)) console.warn("[Button] Icon-only buttons must have an accessible label. Add `aria-label`, `aria-labelledby`, or `title` to describe the button action.");
}, [
_isSplit,
colorScheme,
iconLeft,
iconRight,
variantProp,
children,
text,
title,
ariaLabel,
ariaLabelledby
]);
const leftIcon = renderButtonIcon(resolvedStartIcon ?? icon, "left", isLoading);
const rightIcon = renderButtonIcon(endIcon, "right");
const contentElement = isLoading ? isIconOnly ? null : renderContent(loadingText, void 0, isTruncated) : renderContent(children, text, isTruncated);
return /* @__PURE__ */ jsx(ButtonProvider, {
value: contextValue,
children: /* @__PURE__ */ jsxs(ButtonBase, {
...finalProps,
children: [
leftIcon,
contentElement,
rightIcon
]
})
});
});
Button.Icon = ButtonIcon;
Button.Text = ButtonText;
Button.displayName = "Button";
//#endregion
export { Button, Button as default, ButtonBase };
globalThis.__vuiVersion__ = "5.3.1"
//# sourceMappingURL=button.js.map