UNPKG

@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
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