You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Mobile/ui/button/index.tsx

433 lines
12 KiB

"use client";
import React = require("react");
import { ActivityIndicator, Pressable, Text, View } from "react-native";
const SCOPE = "BUTTON";
const Root = withStyleContext(Pressable, SCOPE);
const UIButton = createButton({
Root: Root,
Text,
Group: View,
Spinner: ActivityIndicator,
Icon: UIIcon,
});
cssInterop(PrimitiveIcon, {
className: {
target: "style",
nativeStyleToProp: {
height: true,
width: true,
fill: true,
color: "classNameColor",
stroke: true,
},
},
});
const buttonStyle = tva({
base: "group/button rounded-3xl bg-primary-500 flex-row items-center justify-center data-[focus-visible=true]:web:outline-none data-[focus-visible=true]:web:ring-2 data-[disabled=true]:opacity-40 gap-2",
variants: {
action: {
primary:
"bg-primary-500 data-[hover=true]:bg-primary-600 data-[active=true]:bg-primary-700 border-primary-300 data-[hover=true]:border-primary-400 data-[active=true]:border-primary-500 data-[focus-visible=true]:web:ring-indicator-info",
secondary:
"bg-secondary-500 border-secondary-300 data-[hover=true]:bg-secondary-600 data-[hover=true]:border-secondary-400 data-[active=true]:bg-secondary-700 data-[active=true]:border-secondary-700 data-[focus-visible=true]:web:ring-indicator-info",
positive:
"bg-success-500 border-success-300 data-[hover=true]:bg-success-600 data-[hover=true]:border-success-400 data-[active=true]:bg-success-700 data-[active=true]:border-success-500 data-[focus-visible=true]:web:ring-indicator-info",
negative:
"bg-error-500 border-error-300 data-[hover=true]:bg-error-600 data-[hover=true]:border-error-400 data-[active=true]:bg-error-700 data-[active=true]:border-error-500 data-[focus-visible=true]:web:ring-indicator-info",
default:
"bg-transparent data-[hover=true]:bg-background-50 data-[active=true]:bg-transparent",
},
variant: {
link: "px-0",
outline:
"bg-transparent border data-[hover=true]:bg-background-50 data-[active=true]:bg-transparent",
solid: "",
},
size: {
xs: "h-8",
sm: "h-9",
md: "h-10",
lg: "h-11",
xl: "p-4 h-16",
"2xl": "p-6 h-20",
"3xl": "p-8 h-24",
},
},
compoundVariants: [
{
action: "primary",
variant: "link",
class:
"px-0 bg-transparent data-[hover=true]:bg-transparent data-[active=true]:bg-transparent",
},
{
action: "secondary",
variant: "link",
class:
"px-0 bg-transparent data-[hover=true]:bg-transparent data-[active=true]:bg-transparent",
},
{
action: "positive",
variant: "link",
class:
"px-0 bg-transparent data-[hover=true]:bg-transparent data-[active=true]:bg-transparent",
},
{
action: "negative",
variant: "link",
class:
"px-0 bg-transparent data-[hover=true]:bg-transparent data-[active=true]:bg-transparent",
},
{
action: "primary",
variant: "outline",
class:
"bg-transparent data-[hover=true]:bg-background-50 data-[active=true]:bg-transparent",
},
{
action: "secondary",
variant: "outline",
class:
"bg-transparent data-[hover=true]:bg-background-50 data-[active=true]:bg-transparent",
},
{
action: "positive",
variant: "outline",
class:
"bg-transparent data-[hover=true]:bg-background-50 data-[active=true]:bg-transparent",
},
{
action: "negative",
variant: "outline",
class:
"bg-transparent data-[hover=true]:bg-background-50 data-[active=true]:bg-transparent",
},
],
});
const buttonTextStyle = tva({
base: "text-typography-0 font-semibold web:select-none",
parentVariants: {
action: {
primary:
"text-primary-600 data-[hover=true]:text-primary-600 data-[active=true]:text-primary-700",
secondary:
"text-typography-500 data-[hover=true]:text-typography-600 data-[active=true]:text-typography-700",
positive:
"text-success-600 data-[hover=true]:text-success-600 data-[active=true]:text-success-700",
negative:
"text-error-600 data-[hover=true]:text-error-600 data-[active=true]:text-error-700",
},
variant: {
link: "underline data-[hover=true]:underline data-[active=true]:underline text-orange-400",
outline: "",
solid:
"text-typography-0 data-[hover=true]:text-typography-0 data-[active=true]:text-typography-0",
},
size: {
xs: "text-xs",
sm: "text-sm",
md: "text-base",
lg: "text-lg",
xl: "text-xl",
"2xl": "text-2xl",
"3xl": "text-3xl",
},
},
parentCompoundVariants: [
{
variant: "solid",
action: "primary",
class:
"text-typography-0 data-[hover=true]:text-typography-0 data-[active=true]:text-typography-0",
},
{
variant: "solid",
action: "secondary",
class:
"text-typography-800 data-[hover=true]:text-typography-800 data-[active=true]:text-typography-800",
},
{
variant: "solid",
action: "positive",
class:
"text-typography-0 data-[hover=true]:text-typography-0 data-[active=true]:text-typography-0",
},
{
variant: "solid",
action: "negative",
class:
"text-typography-0 data-[hover=true]:text-typography-0 data-[active=true]:text-typography-0",
},
{
variant: "outline",
action: "primary",
class:
"text-primary-500 data-[hover=true]:text-primary-500 data-[active=true]:text-primary-500",
},
{
variant: "outline",
action: "secondary",
class:
"text-typography-500 data-[hover=true]:text-primary-600 data-[active=true]:text-typography-700",
},
{
variant: "outline",
action: "positive",
class:
"text-primary-500 data-[hover=true]:text-primary-500 data-[active=true]:text-primary-500",
},
{
variant: "outline",
action: "negative",
class:
"text-primary-500 data-[hover=true]:text-primary-500 data-[active=true]:text-primary-500",
},
],
});
const buttonIconStyle = tva({
base: "fill-none",
parentVariants: {
variant: {
link: "data-[hover=true]:underline data-[active=true]:underline",
outline: "",
solid:
"text-typography-0 data-[hover=true]:text-typography-0 data-[active=true]:text-typography-0",
},
size: {
xs: "h-3.5 w-3.5",
sm: "h-4 w-4",
md: "h-[18px] w-[18px]",
lg: "h-[18px] w-[18px]",
xl: "h-5 w-5",
"2xl": "h-8 w-8",
"3xl": "h-10 w-10",
},
action: {
primary:
"text-primary-600 data-[hover=true]:text-primary-600 data-[active=true]:text-primary-700",
secondary:
"text-typography-500 data-[hover=true]:text-typography-600 data-[active=true]:text-typography-700",
positive:
"text-success-600 data-[hover=true]:text-success-600 data-[active=true]:text-success-700",
negative:
"text-error-600 data-[hover=true]:text-error-600 data-[active=true]:text-error-700",
},
},
parentCompoundVariants: [
{
variant: "solid",
action: "primary",
class:
"text-typography-0 data-[hover=true]:text-typography-0 data-[active=true]:text-typography-0",
},
{
variant: "solid",
action: "secondary",
class:
"text-typography-800 data-[hover=true]:text-typography-800 data-[active=true]:text-typography-800",
},
{
variant: "solid",
action: "positive",
class:
"text-typography-0 data-[hover=true]:text-typography-0 data-[active=true]:text-typography-0",
},
{
variant: "solid",
action: "negative",
class:
"text-typography-0 data-[hover=true]:text-typography-0 data-[active=true]:text-typography-0",
},
],
});
const buttonGroupStyle = tva({
base: "",
variants: {
space: {
xs: "gap-1",
sm: "gap-2",
md: "gap-3",
lg: "gap-4",
xl: "gap-5",
"2xl": "gap-6",
"3xl": "gap-7",
"4xl": "gap-8",
},
isAttached: {
true: "gap-0",
},
flexDirection: {
row: "flex-row",
column: "flex-col",
"row-reverse": "flex-row-reverse",
"column-reverse": "flex-col-reverse",
},
},
});
type IButtonProps = Omit<
React.ComponentPropsWithoutRef<typeof UIButton>,
"context"
> &
VariantProps<typeof buttonStyle> & { className?: string };
const Button = React.forwardRef<
React.ElementRef<typeof UIButton>,
IButtonProps
>(
(
{ className, variant = "solid", size = "md", action = "primary", ...props },
ref
) => {
return (
<UIButton
ref={ref}
{...props}
className={buttonStyle({ variant, size, action, class: className })}
context={{ variant, size, action }}
/>
);
}
);
type IButtonTextProps = React.ComponentPropsWithoutRef<typeof UIButton.Text> &
VariantProps<typeof buttonTextStyle> & { className?: string };
const ButtonText = React.forwardRef<
React.ElementRef<typeof UIButton.Text>,
IButtonTextProps
>(({ className, variant, size, action, ...props }, ref) => {
const {
variant: parentVariant,
size: parentSize,
action: parentAction,
} = useStyleContext(SCOPE);
return (
<UIButton.Text
ref={ref}
{...props}
className={buttonTextStyle({
parentVariants: {
variant: parentVariant,
size: parentSize,
action: parentAction,
},
variant,
size,
action,
class: className,
})}
/>
);
});
const ButtonSpinner = UIButton.Spinner;
type IButtonIcon = React.ComponentPropsWithoutRef<typeof UIButton.Icon> &
VariantProps<typeof buttonIconStyle> & {
className?: string | undefined;
as?: React.ElementType;
height?: number;
width?: number;
};
const ButtonIcon = React.forwardRef<
React.ElementRef<typeof UIButton.Icon>,
IButtonIcon
>(({ className, size, ...props }, ref) => {
const {
variant: parentVariant,
size: parentSize,
action: parentAction,
} = useStyleContext(SCOPE);
if (typeof size === "number") {
return (
<UIButton.Icon
ref={ref}
{...props}
className={buttonIconStyle({ class: className })}
size={size}
/>
);
} else if (
(props.height !== undefined || props.width !== undefined) &&
size === undefined
) {
return (
<UIButton.Icon
ref={ref}
{...props}
className={buttonIconStyle({ class: className })}
/>
);
}
return (
<UIButton.Icon
{...props}
className={buttonIconStyle({
parentVariants: {
size: parentSize,
variant: parentVariant,
action: parentAction,
},
size,
class: className,
})}
ref={ref}
/>
);
});
type IButtonGroupProps = React.ComponentPropsWithoutRef<typeof UIButton.Group> &
VariantProps<typeof buttonGroupStyle>;
const ButtonGroup = React.forwardRef<
React.ElementRef<typeof UIButton.Group>,
IButtonGroupProps
>(
(
{
className,
space = "md",
isAttached = false,
flexDirection = "column",
...props
},
ref
) => {
return (
<UIButton.Group
className={buttonGroupStyle({
class: className,
space,
isAttached,
flexDirection,
})}
{...props}
ref={ref}
/>
);
}
);
Button.displayName = "Button";
ButtonText.displayName = "ButtonText";
ButtonSpinner.displayName = "ButtonSpinner";
ButtonIcon.displayName = "ButtonIcon";
ButtonGroup.displayName = "ButtonGroup";
export { Button, ButtonText, ButtonSpinner, ButtonIcon, ButtonGroup };
export type ButtonActions = keyof typeof buttonStyle.variants.action;