| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141 |
- import { ActivityIndicator, Icon } from '@ant-design/react-native';
- import { clsx } from 'clsx';
- import { Link } from 'expo-router';
- import React, { useEffect, useState } from 'react';
- import { Pressable, Text, View } from 'react-native';
- import { Colors } from '@/constants/theme';
- import { isPromise } from '@/utils/tsutils';
- import type {IconNames} from '@ant-design/react-native/lib/icon';
- import type { Href} from 'expo-router';
- import type { ViewStyle } from 'react-native';
- interface UIButtonProps {
- children?: string | React.ReactNode;
- title?: string | React.ReactNode;
- onPress?: () => PromiseLike<unknown> | unknown;
- disabled?: boolean;
- className?: string;
- textClassName?: string;
- href?: Href;
- type?: 'primary' | 'second' | 'link';
- icon?: IconNames | React.ReactNode;
- style?: ViewStyle;
- loading?: boolean;
- }
- type ButtonType = UIButtonProps['type'];
- function getIconColor(type: ButtonType, disabled: boolean) {
- if (disabled) return Colors['on-surface']['variant'];
- if (type === 'primary') return '#fff';
- if (type === 'second') return Colors.secondary.DEFAULT;
- return Colors.primary.DEFAULT;
- }
- function ButtonLabel({
- type,
- disabled,
- textClassName,
- children,
- loading,
- }: {
- type: ButtonType;
- disabled: boolean;
- textClassName?: string;
- children: React.ReactNode;
- loading: boolean;
- }) {
- const labelClass = clsx(textClassName, 'text-sm font-semibold', {
- 'text-on-primary': !disabled && type === 'primary',
- 'text-primary': !disabled && type !== 'primary' && type !== 'second',
- 'text-secondary': !disabled && type === 'second',
- 'text-on-surface-variant/65 font-normal': disabled,
- });
- if (!loading) return <Text className={labelClass}>{children}</Text>;
- return (
- <View className="flex-row justify-center items-center">
- <ActivityIndicator color={disabled ? '#94a3b8' : Colors['on-primary']['DEFAULT']} />
- <Text className={labelClass}>{children}</Text>
- </View>
- );
- }
- export default function UIButton({
- onPress,
- href,
- type,
- icon,
- className,
- style,
- disabled,
- children,
- title,
- textClassName,
- loading,
- }: UIButtonProps) {
- const [busy, setBusy] = useState(loading);
- const isDisabled = Boolean(disabled || busy);
- useEffect(() => {
- setBusy(loading);
- }, [loading]);
- const label = children ?? title;
- const handlePress = () => {
- if (isDisabled) return;
- const res = onPress?.();
- if (isPromise(res)) {
- setBusy(true);
- (res as Promise<unknown>).finally(() => setBusy(false));
- }
- };
- const inner = (
- <Pressable
- className={clsx(
- 'flex-row justify-center items-center rounded-xl py-1.5 border-2 opacity-100 active:opacity-75 active:scale-[0.95]',
- className,
- {
- 'bg-primary border-primary': type === 'primary',
- 'bg-surface border-primary/25': !type,
- 'bg-surface border-on-surface/25': type === 'second',
- 'bg-transparent border-transparent': type === 'link',
- 'bg-primary-fixed-dim border-primary-fixed-dim': isDisabled && type === 'primary',
- 'bg-gray-300 border-gray-300': isDisabled && !type,
- 'bg-gray-400 border-gray-200': isDisabled && type === 'second',
- }
- )}
- disabled={isDisabled}
- onPress={handlePress}
- style={style}
- >
- {typeof icon === 'string' ? (
- <Icon
- name={icon as IconNames}
- size={20}
- style={{ marginRight: 4, color: getIconColor(type, isDisabled) }}
- />
- ) : (
- icon
- )}
- {typeof label === 'string' ? (
- <ButtonLabel type={type} disabled={isDisabled} loading={!!busy} textClassName={textClassName}>
- {label}
- </ButtonLabel>
- ) : (
- label
- )}
- </Pressable>
- );
- if (typeof href !== 'undefined') {
- return (
- <Link href={href} asChild>
- {inner}
- </Link>
- );
- }
- return inner;
- }
|