@tamagui/button
Version:
233 lines • 6.96 kB
JavaScript
import { getFontSize } from "@tamagui/font-size";
import { getButtonSized } from "@tamagui/get-button-sized";
import { withStaticProperties } from "@tamagui/helpers";
import { useGetThemedIcon } from "@tamagui/helpers-tamagui";
import { ButtonNestingContext, ThemeableStack } from "@tamagui/stacks";
import { SizableText, wrapChildrenInText } from "@tamagui/text";
import { createStyledContext, getVariableValue, spacedChildren, styled, useProps } from "@tamagui/web";
import { useContext } from "react";
import { jsx } from "react/jsx-runtime";
const ButtonContext = createStyledContext({
// keeping these here means they work with styled() passing down color to text
color: void 0,
ellipse: void 0,
fontFamily: void 0,
fontSize: void 0,
fontStyle: void 0,
fontWeight: void 0,
letterSpacing: void 0,
maxFontSizeMultiplier: void 0,
size: void 0,
textAlign: void 0,
variant: void 0
}),
BUTTON_NAME = "Button",
ButtonFrame = styled(ThemeableStack, {
name: BUTTON_NAME,
tag: "button",
context: ButtonContext,
role: "button",
focusable: !0,
variants: {
unstyled: {
false: {
size: "$true",
justifyContent: "center",
alignItems: "center",
flexWrap: "nowrap",
flexDirection: "row",
cursor: "pointer",
hoverTheme: !0,
pressTheme: !0,
backgrounded: !0,
borderWidth: 1,
borderColor: "transparent",
focusVisibleStyle: {
outlineColor: "$outlineColor",
outlineStyle: "solid",
outlineWidth: 2
}
}
},
variant: {
outlined: {
backgroundColor: "transparent",
borderWidth: 2,
borderColor: "$borderColor",
hoverStyle: {
backgroundColor: "transparent",
borderColor: "$borderColorHover"
},
pressStyle: {
backgroundColor: "transparent",
borderColor: "$borderColorPress"
},
focusVisibleStyle: {
backgroundColor: "transparent",
borderColor: "$borderColorFocus"
}
}
},
size: {
"...size": getButtonSized,
":number": getButtonSized
},
disabled: {
true: {
pointerEvents: "none"
}
}
},
defaultVariants: {
unstyled: process.env.TAMAGUI_HEADLESS === "1"
}
}),
ButtonText = styled(SizableText, {
name: "Button",
context: ButtonContext,
variants: {
unstyled: {
false: {
userSelect: "none",
cursor: "pointer",
// flexGrow 1 leads to inconsistent native style where text pushes to start of view
flexGrow: 0,
flexShrink: 1,
ellipse: !0,
color: "$color"
}
}
},
defaultVariants: {
unstyled: process.env.TAMAGUI_HEADLESS === "1"
}
}),
ButtonIcon = props => {
const {
children,
scaleIcon = 1
} = props,
{
size,
color
} = useContext(ButtonContext),
iconSize = (typeof size == "number" ? size * 0.5 : getFontSize(size)) * scaleIcon;
return useGetThemedIcon({
size: iconSize,
color
})(children);
},
ButtonComponent = ButtonFrame.styleable(function (props, ref) {
const {
props: buttonProps
} = useButton(props);
return /* @__PURE__ */jsx(ButtonFrame, {
...buttonProps,
ref
});
}),
buttonStaticConfig = {
inlineProps: /* @__PURE__ */new Set([
// text props go here (can't really optimize them, but we never fully extract button anyway)
// may be able to remove this entirely, as the compiler / runtime have gotten better
"color", "fontWeight", "fontSize", "fontFamily", "fontStyle", "letterSpacing", "textAlign", "unstyled"])
},
Button2 = withStaticProperties(ButtonComponent, {
Text: ButtonText,
Icon: ButtonIcon
});
function useButton({
textProps,
...propsIn
}, {
Text = Button2.Text
} = {
Text: Button2.Text
}) {
const isNested = useContext(ButtonNestingContext),
propsActive = useProps(propsIn, {
noNormalize: !0,
noExpand: !0
}),
{
icon,
iconAfter,
space,
spaceFlex,
scaleIcon = 1,
scaleSpace = 0.66,
separator,
noTextWrap,
fontFamily,
fontSize,
fontWeight,
fontStyle,
letterSpacing,
tag,
ellipse,
maxFontSizeMultiplier,
...restProps
} = propsActive,
size = propsActive.size || (propsActive.unstyled ? void 0 : "$true"),
color = propsActive.color,
iconSize = (typeof size == "number" ? size * 0.5 : getFontSize(size, {
font: fontFamily?.[0] === "$" ? fontFamily : void 0
})) * scaleIcon,
getThemedIcon = useGetThemedIcon({
size: iconSize,
color
}),
[themedIcon, themedIconAfter] = [icon, iconAfter].map(getThemedIcon),
spaceSize = space ?? getVariableValue(iconSize) * scaleSpace,
contents = noTextWrap ? [propsIn.children] : wrapChildrenInText(Text, {
children: propsIn.children,
fontFamily,
fontSize,
textProps,
fontWeight,
fontStyle,
letterSpacing,
ellipse,
maxFontSizeMultiplier
}, Text === ButtonText && propsActive.unstyled !== !0 ? {
unstyled: process.env.TAMAGUI_HEADLESS === "1",
size
} : void 0),
inner = spacedChildren({
// a bit arbitrary but scaling to font size is necessary so long as button does
space: spaceSize,
spaceFlex,
separator,
direction: propsActive.flexDirection === "column" || propsActive.flexDirection === "column-reverse" ? "vertical" : "horizontal",
children: [themedIcon, ...contents, themedIconAfter]
}),
props = {
size,
...(propsIn.disabled && {
// in rnw - false still has keyboard tabIndex, undefined = not actually focusable
focusable: void 0,
// even with tabIndex unset, it will keep focusVisibleStyle on web so disable it here
focusVisibleStyle: {
borderColor: "$background"
}
}),
// fixes SSR issue + DOM nesting issue of not allowing button in button
tag: tag ?? (isNested ? "span" :
// defaults to <a /> when accessibilityRole = link
// see https://github.com/tamagui/tamagui/issues/505
propsActive.accessibilityRole === "link" || propsActive.role === "link" ? "a" : "button"),
...restProps,
children: /* @__PURE__ */jsx(ButtonNestingContext.Provider, {
value: !0,
children: inner
}),
// forces it to be a runtime pressStyle so it passes through context text colors
disableClassName: !0
};
return {
spaceSize,
isNested,
props
};
}
export { Button2 as Button, ButtonContext, ButtonFrame, ButtonIcon, ButtonText, buttonStaticConfig, useButton };