UNPKG

@navinc/base-react-components

Version:
190 lines 10.6 kB
var __rest = (this && this.__rest) || function (s, e) { var t = {}; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]]; } return t; }; import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime"; import { forwardRef } from 'react'; import { cva } from 'class-variance-authority'; import { cn } from '../../cn.js'; import { styledBackwardsCompatibility } from '../../styled-backwards-compatibility.js'; import { Slot } from '../slot/slot.js'; import { LoadingDots } from '../../loading-dots.js'; import { isFragment } from 'react-is'; import { Children, cloneElement } from 'react'; import { addClassNameTo } from '../../add-classname-to.js'; /** Responsible for wrapping all button text to properly underline on hover for the plain variants */ export const ButtonTextContainer = addClassNameTo('span', /* cn */ 'group-hover/wf-button:underline group-disabled/wf-button:no-underline'); // TODO: double check linear gradients against figma once we upgrade to Tailwind 4+ const buttonVariants = cva('group/wf-button inline-flex justify-center items-center text-center transition-all duration-100 ease-in-out rounded-200 bg-linear-[25deg,primary_5%] disabled:from-transparent disabled:to-transparent', { variants: { variant: { default: [ 'bg-surfaceContainerHighest text-onSurface shadow-elevation1', 'hover:bg-gradient-to-b hover:from-white/0 hover:to-[rgb(27,27,31)]/4 hover:to-100%', 'active:bg-gradient-to-b active:from-[rgb(27,27,31)]/8 active:to-[rgb(27,27,31)]/8 active:to-100%', 'active:shadow-buttonActive active:text-onSurface/60 active:scale-[0.96]', 'disabled:shadow-none disabled:bg-[rgb(27,27,31)]/4 disabled:text-onSurfaceDim disabled:hover:bg-none', ], plain: [ 'bg-transparent gap-25 [&>.invisible]:gap-25', 'text-onSurfaceVariant active:opacity-60', 'disabled:text-onSurfaceDim disabled:no-underline', ], primaryPlain: [ 'bg-transparent gap-25 [&>.invisible]:gap-25', 'text-primary active:opacity-60', 'disabled:text-onSurfaceDim disabled:no-underline', ], prime: [ 'bg-prime text-onPrime', 'bg-gradient-to-b from-[rgb(106,209,227)]/30 from-25% to-[rgb(48,71,119)]/30 to-[99.22%]', 'shadow-[0_0_0_1px_rgb(66_111_156/100%),0_1px_0.5px_0_rgb(220_248_253/30%)_inset,0_-1px_0.5px_0_rgb(18_16_39/30%)_inset,0_1px_0.5px_0_rgb(255_255_255/50%)_inset,0_0_0_1px_rgb(220_219_220/100%)]', 'hover:bg-gradient-to-b hover:from-[rgb(189,241,250)]/30 hover:from-75% hover:via-[rgb(159,231,244)]/30 hover:via-25% hover:to-[rgb(220,248,253)]/0', 'active:bg-gradient-to-b active:from-[rgb(18,16,39)]/30 active:from-25% active:via-[rgb(66,111,156)]/30 active:via-75% active:to-[rgb(66,111,156)]/0', 'active:shadow-[0_0_0_1px_#000] active:text-onPrime/60 active:scale-[0.96]', 'disabled:shadow-none disabled:bg-[rgb(27,27,31)]/4 disabled:text-onSurfaceDim', ], primary: [ 'bg-primary text-onPrimary', 'shadow-[0_1px_0.5px_0_rgb(255_255_255/60%)_inset,0_-1px_0.5px_0_rgb(0_0_0/20%)_inset,0_0_0_1px_hsl(var(--twc-onPrimaryContainer))]', 'hover:bg-gradient-to-b hover:from-[rgb(152,145,222)]/30 hover:from-25% hover:via-[rgb(94,77,178)]/30 hover:via-75% hover:to-[rgb(94,77,178)]/0', 'active:bg-gradient-to-b active:from-[rgb(20,6,56)]/30 active:from-25% active:via-[rgb(67,52,135)]/30 active:via-75% active:to-[rgb(67,52,135)]/0', 'active:shadow-[0_0_0_1px_#000] active:text-onPrimary/60 active:scale-[0.96]', 'disabled:shadow-none disabled:bg-[rgb(27,27,31)]/4 disabled:text-onSurfaceDim', ], primaryCritical: [ 'bg-errorBright text-onError', 'shadow-[0_1px_0.5px_0_rgb(255_255_255/60%)_inset,0_-1px_0.5px_0_rgb(0_0_0/20%)_inset,0_0_0_1px_hsl(var(--twc-onErrorContainer))]', 'hover:bg-gradient-to-b hover:from-[rgb(242,139,130)]/30 hover:from-25% hover:via-[rgb(216,48,37)]/30 hover:via-75% hover:to-[rgb(216,48,37)]/0', 'active:bg-gradient-to-b active:from-[rgb(27,27,31)]/8 active:to-[rgb(27,27,31)]/8', 'active:text-onError/60 active:scale-[0.96]', 'disabled:shadow-none disabled:bg-[rgb(27,27,31)]/4 disabled:text-onSurfaceDim', ], }, density: { tight: ['caption1-emphasized'], standard: ['body2-emphasized'], loose: ['body1-emphasized'], }, }, compoundVariants: [ // Default sizing for non-plain variants { variant: ['default', 'prime', 'primary', 'primaryCritical'], density: 'tight', class: [ 'min-w-1000 min-h-350 px-100 py-75 gap-25 [&>.invisible]:gap-25', '[&_.nav-icon]:w-[var(--nav-icon-size-small)] [&_.nav-icon]:h-[var(--nav-icon-size-small)] [&_.nav-icon]:text-[length:--nav-icon-size-small]', ], }, { variant: ['default', 'prime', 'primary', 'primaryCritical'], density: 'standard', class: [ 'min-w-1000 min-h-400 px-100 py-75 gap-50 [&>.invisible]:gap-50', '[&_.nav-icon]:w-[var(--nav-icon-size-medium)] [&_.nav-icon]:h-[var(--nav-icon-size-medium)] [&_.nav-icon]:text-[length:--nav-icon-size-medium]', ], }, { variant: ['default', 'prime', 'primary', 'primaryCritical'], density: 'loose', class: [ 'min-w-1000 min-h-600 px-200 py-150 gap-100 [&>.invisible]:gap-100', '[&_.nav-icon]:w-[var(--nav-icon-size-large)] [&_.nav-icon]:h-[var(--nav-icon-size-large)] [&_.nav-icon]:text-[length:--nav-icon-size-large]', ], }, // Override sizing and typography for plain variants { variant: ['plain', 'primaryPlain'], density: ['tight', 'standard', 'loose'], class: 'min-w-[auto] min-h-[auto] p-0', }, // Override typography for plain variants in standard and loose density { variant: ['plain', 'primaryPlain'], density: ['standard', 'loose'], class: 'body2-emphasized', }, ], defaultVariants: { variant: 'default', density: 'standard', }, }); // automatically wrap text nodes in a ButtonTextContainer component const wrapTextNodesInTextWrap = (children) => { if (!children) { return children; } const processedChildren = []; let group = []; // flushGroup wraps and pushes the group items into the processedChildren array and resets the group array const flushGroup = () => { if (group.length) { processedChildren.push(_jsx(ButtonTextContainer, { "data-testid": "button-text-container", children: group }, `text-wrap.${processedChildren.length}`)); group = []; } }; Children.toArray(children).forEach((child) => { // group multiple primitives together to be wrapped in a single ButtonTextContainer if (typeof child === 'string' || typeof child === 'number') { group.push(child); return; } // next item is not a primitive, flush the group (push items into to processedChildren array) flushGroup(); // fragments are processed recursively, this is to support `<Button><>text</></Button>` if (isFragment(child)) { processedChildren.push(cloneElement(child, { children: wrapTextNodesInTextWrap(child.props.children), })); return; } processedChildren.push(child); }); // flush any lingering items (push items into processedChildren array) flushGroup(); return processedChildren; }; /** * Wayfinder Button * * @example `asChild` prop renders the button as the immediate child of the button element. * * ```tsx * <Button asChild> * <a href="https://nav.com" target="_blank" rel="noreferrer"> * Link Rendered as a Button * </a> * </Button> * ``` * This results in the following HTML: * ```html * <a class="button-classes" href="https://nav.com" target="_blank" rel="noreferrer"> * Link Rendered as a Button * </a> * ``` * * @caveat text children of the button will be wrapped in a `ButtonTextContainer` component to properly underline on hover * for the plain variants. If any children are not text, they will not be wrapped automatically and no underline will * be present. */ export const Button = styledBackwardsCompatibility(forwardRef((_a, ref) => { var { variant, density, loading, children, asChild, className } = _a, props = __rest(_a, ["variant", "density", "loading", "children", "asChild", "className"]); // we only need to wrap text nodes in a ButtonTextContainer for plain variants to avoid underlining icons const isPlainVariant = variant === 'plain' || variant === 'primaryPlain'; if (loading) { return (_jsx(Slot, Object.assign({ asChild: asChild, defaultComponent: "button", child: children, ref: ref, className: cn(buttonVariants({ variant, density, className })) }, props, (asChild ? {} : { disabled: true }), { children: (child) => (_jsxs(_Fragment, { children: [_jsx("div", { className: "invisible flex flex-row items-center justify-center", children: isPlainVariant ? wrapTextNodesInTextWrap(child) : child }), _jsx("div", { className: "flex items-center justify-center absolute", "data-testid": "button-loading-dots", children: _jsx(LoadingDots, { className: "scale-75" }) })] })) }))); } return (_jsx(Slot, Object.assign({ defaultComponent: "button", asChild: asChild, child: children, ref: ref, className: cn(buttonVariants({ variant, density, className })) }, props, { children: (child) => _jsx(_Fragment, { children: isPlainVariant ? wrapTextNodesInTextWrap(child) : child }) }))); })); export const ButtonInheritColor = addClassNameTo(Button, 'text-inherit'); //# sourceMappingURL=button.js.map