UIButton.tsx 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293
  1. import { Colors } from '@/constants/theme';
  2. import { isPromise } from '@/utils/tsutils';
  3. import { ActivityIndicator, Icon } from '@ant-design/react-native';
  4. import { type IconNames } from '@ant-design/react-native/lib/icon';
  5. import { clsx } from 'clsx';
  6. import { Href, Link } from "expo-router";
  7. import React, { useCallback, useEffect, useMemo, useState } from "react";
  8. import { Pressable, Text, View, ViewStyle } from "react-native";
  9. interface UIButtonProps {
  10. children?: string | React.ReactNode;
  11. title?: string | React.ReactNode;
  12. onPress?: () => PromiseLike<unknown> | unknown;
  13. disabled?: boolean;
  14. className?: string;
  15. textClassName?: string;
  16. href?: Href
  17. type?: 'primary' | 'second' | 'link'
  18. icon?: IconNames | React.ReactNode;
  19. style?: ViewStyle;
  20. loading?: boolean;
  21. }
  22. function ButtonTextChild({ type, disabled, textClassName, children, loading }: UIButtonProps) {
  23. return loading ? <View className='flex-row justify-center items-center'>
  24. <ActivityIndicator color={disabled ? '#888' : Colors['on-primary']['DEFAULT']} />
  25. <Text className={clsx(
  26. textClassName,
  27. 'text-sm font-semibold', {
  28. 'text-on-primary': !disabled && type === 'primary',
  29. 'text-primary': !disabled && type !== 'primary',
  30. 'text-secondary': !disabled && type === 'second',
  31. 'text-on-surface': !disabled && !type,
  32. 'text-on-surface-variant/65 font-normal': disabled,
  33. })}>{children}</Text>
  34. </View> : <Text className={clsx(
  35. textClassName,
  36. 'text-sm font-semibold', {
  37. 'text-on-primary': !disabled && type === 'primary',
  38. 'text-primary': !disabled && type !== 'primary',
  39. 'text-secondary': !disabled && type === 'second',
  40. 'text-on-surface': !disabled && !type,
  41. 'text-on-surface-variant/65 font-normal': disabled,
  42. })}>{children}</Text>
  43. }
  44. export default function UIButton({ onPress, href, type, icon, className, style, disabled, children, title, textClassName, loading }: UIButtonProps) {
  45. const [ding, setDing] = useState(loading);
  46. const isDisabled = Boolean(disabled || ding);
  47. useEffect(() => {
  48. setDing(loading);
  49. }, [loading])
  50. children = children || title;
  51. const handlePress = useCallback(() => {
  52. if (isDisabled) {
  53. return;
  54. }
  55. const res = onPress?.() as Promise<any> || undefined;
  56. if (isPromise(res)) {
  57. setDing(true);
  58. res.finally(() => {
  59. setDing(false);
  60. })
  61. }
  62. }, [isDisabled, onPress]);
  63. const inner = useMemo(() => <Pressable
  64. className={clsx('flex-row justify-center items-center rounded-xl py-1.5 border-2 opacity-100 active:opacity-75 active:scale-95',
  65. className, {
  66. 'bg-primary border-primary': type === 'primary',
  67. 'bg-surface border-primary/25': !type,
  68. 'bg-surface border-on-surface/25': type === 'second',
  69. 'bg-transparent border-transparent': type === 'link',
  70. 'bg-primary-fixed-dim border-primary-fixed-dim': isDisabled && type === 'primary',
  71. 'bg-gray-300 border-gray-300': isDisabled && !type,
  72. 'bg-bg-gray-400 border-gray-200': isDisabled && type === 'second'
  73. })}
  74. disabled={isDisabled}
  75. onPress={handlePress}
  76. style={style}>
  77. {typeof icon === 'string' ? <Icon name={icon as IconNames} size={20} style={{ marginRight: 4, color: isDisabled ? Colors['on-surface']['variant'] : type === 'primary' ? '#fff' : type === 'second' ? Colors.secondary.DEFAULT : Colors.primary.DEFAULT }} /> : icon}
  78. {typeof children == 'string' ? <ButtonTextChild type={type} disabled={isDisabled} loading={ding} textClassName={textClassName}>{children}</ButtonTextChild> : children}
  79. </Pressable >, [children, className, ding, handlePress, icon, isDisabled, style, textClassName, type])
  80. if (typeof href !== 'undefined') {
  81. return <Link href={href} asChild>
  82. {inner}
  83. </Link>
  84. }
  85. return inner;
  86. }