@navinc/base-react-components
Version:
Nav's Pattern Library
190 lines • 10.6 kB
JavaScript
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