RN组件库 - Button 组件

从零构建 React Native 组件库,作为一个前端er~谁不想拥有一个自己的组件库呢

1、定义 Button 基本类型 type.ts

import type {StyleProp, TextStyle, ViewProps} from 'react-native';
import type {TouchableOpacityProps} from '../TouchableOpacity/type';
import type Loading from '../Loading';

// 五种按钮类型
export type ButtonType =
  | 'primary'
  | 'success'
  | 'warning'
  | 'danger'
  | 'default';
// 四种按钮大小
export type ButtonSize = 'large' | 'small' | 'mini' | 'normal';
// 加载中组件类型
type LoadingProps = React.ComponentProps<typeof Loading>;
// 按钮的基本属性
// extends Pick的作用是:
// 继承父类型的属性和方法:通过extends关键字,子类型可以继承父类型的所有属性和方法。
// 选取父类型的特定属性:通过Pick工具类型,从父类型中选取需要的属性,并将其添加到子类型中。
export interface ButtonProps
  extends Pick<ViewProps, 'style' | 'testID'>,
    Pick<
      TouchableOpacityProps,
      'onPress' | 'onLongPress' | 'onPressIn' | 'onPressOut'
    > {
  /**
   * 类型,可选值为 primary success warning danger
   * @default default
   */
  type?: ButtonType;
  /**
   * 尺寸,可选值为 large small mini
   * @default normal
   */
  size?: ButtonSize;
  /**
   * 按钮颜色,支持传入 linear-gradient 渐变色
   */
  color?: string;
  /**
   * 左侧图标名称或自定义图标组件
   */
  icon?: React.ReactNode;
  /**
   * 图标展示位置,可选值为 right
   * @default left
   */
  iconPosition?: 'left' | 'right';
  /**
   * 是否为朴素按钮
   */
  plain?: boolean;
  /**
   * 是否为方形按钮
   */
  square?: boolean;
  /**
   * 是否为圆形按钮
   */
  round?: boolean;
  /**
   * 是否禁用按钮
   */
  disabled?: boolean;
  /**
   * 是否显示为加载状态
   */
  loading?: boolean;
  /**
   * 加载状态提示文字
   */
  loadingText?: string;
  /**
   * 加载图标类型
   */
  loadingType?: LoadingProps['type'];
  /**
   * 加载图标大小
   */
  loadingSize?: number;
  textStyle?: StyleProp<TextStyle>;
  children?: React.ReactNode;
}

2、动态生成样式对象style.ts

import {StyleSheet} from 'react-native';
import type {ViewStyle, TextStyle} from 'react-native';
import type {ButtonType, ButtonSize} from './type';

type Params = {
  type: ButtonType;
  size: ButtonSize;
  plain?: boolean;
};

type Styles = {
  button: ViewStyle;
  disabled: ViewStyle;
  plain: ViewStyle;
  round: ViewStyle;
  square: ViewStyle;
  text: TextStyle;
};

const createStyle = (
  theme: DiceUI.Theme,
  {type, size, plain}: Params,
): Styles => {
  // Record 是一种高级类型操作,用于创建一个对象类型
  // 其中键的类型由第一个参数指定(ButtonType),值的类型由第二个参数指定(ViewStyle)
  const buttonTypeStyleMaps: Record<ButtonType, ViewStyle> = {
    default: {
      backgroundColor: theme.button_default_background_color,
      borderColor: theme.button_default_border_color,
      borderStyle: 'solid',
      borderWidth: theme.button_border_width,
    },
    danger: {
      backgroundColor: theme.button_danger_background_color,
      borderColor: theme.button_danger_border_color,
      borderStyle: 'solid',
      borderWidth: theme.button_border_width,
    },
    primary: {
      backgroundColor: theme.button_primary_background_color,
      borderColor: theme.button_primary_border_color,
      borderStyle: 'solid',
      borderWidth: theme.button_border_width,
    },
    success: {
      backgroundColor: theme.button_success_background_color,
      borderColor: theme.button_success_border_color,
      borderStyle: 'solid',
      borderWidth: theme.button_border_width,
    },
    warning: {
      backgroundColor: theme.button_warning_background_color,
      borderColor: theme.button_warning_border_color,
      borderStyle: 'solid',
      borderWidth: theme.button_border_width,
    },
  };

  const buttonSizeStyleMaps: Record<ButtonSize, ViewStyle> = {
    normal: {},
    small: {
      height: theme.button_small_height,
    },
    large: {
      height: theme.button_large_height,
      width: '100%',
    },
    mini: {
      height: theme.button_mini_height,
    },
  };

  const contentPadding: Record<ButtonSize, ViewStyle> = {
    normal: {
      paddingHorizontal: theme.button_normal_padding_horizontal,
    },
    small: {
      paddingHorizontal: theme.button_small_padding_horizontal,
    },
    large: {},
    mini: {
      paddingHorizontal: theme.button_mini_padding_horizontal,
    },
  };

  const textSizeStyleMaps: Record<ButtonSize, TextStyle> = {
    normal: {
      fontSize: theme.button_normal_font_size,
    },
    large: {
      fontSize: theme.button_default_font_size,
    },
    mini: {
      fontSize: theme.button_mini_font_size,
    },
    small: {
      fontSize: theme.button_small_font_size,
    },
  };

  const textTypeStyleMaps: Record<ButtonType, TextStyle> = {
    default: {
      color: theme.button_default_color,
    },
    danger: {
      color: plain
        ? theme.button_danger_background_color
        : theme.button_danger_color,
    },
    primary: {
      color: plain
        ? theme.button_primary_background_color
        : theme.button_primary_color,
    },
    success: {
      color: plain
        ? theme.button_success_background_color
        : theme.button_success_color,
    },
    warning: {
      color: plain
        ? theme.button_warning_background_color
        : theme.button_warning_color,
    },
  };

  return StyleSheet.create<Styles>({
    button: {
      alignItems: 'center',
      borderRadius: theme.button_border_radius,
      flexDirection: 'row',
      height: theme.button_default_height,
      justifyContent: 'center',
      overflow: 'hidden',
      position: 'relative',
      ...buttonTypeStyleMaps[type],
      ...buttonSizeStyleMaps[size],
      ...contentPadding[size],
    },
    disabled: {
      opacity: theme.button_disabled_opacity,
    },
    plain: {
      backgroundColor: theme.button_plain_background_color,
    },

    round: {
      borderRadius: theme.button_round_border_radius,
    },

    square: {
      borderRadius: 0,
    },

    text: {
      ...textTypeStyleMaps[type],
      ...textSizeStyleMaps[size],
    },
  });
};

export default createStyle;

3、实现 Button 组件

import React, {FC, memo} from 'react';
import {View, ViewStyle, StyleSheet, Text, TextStyle} from 'react-native';
import TouchableOpacity from '../TouchableOpacity';
import {useThemeFactory} from '../Theme';
import Loading from '../Loading';
import createStyle from './style';
import type {ButtonProps} from './type';

const Button: FC<ButtonProps> = memo(props => {
  const {
    type = 'default',
    size = 'normal',
    loading,
    loadingText,
    loadingType,
    loadingSize,
    icon,
    iconPosition = 'left',
    color,
    plain,
    square,
    round,
    disabled,
    textStyle,
    children,
    // 对象的解构操作,在末尾使用...会将剩余的属性都收集到 rest 对象中。
    ...rest
  } = props;
  // useThemeFactory 调用 createStyle 函数根据入参动态生成一个 StyleSheet.create<Styles> 对象
  const {styles} = useThemeFactory(createStyle, {type, size, plain});
  const text = loading ? loadingText : children;
  // 将属性合并到一个新的样式对象中,并返回这个新的样式对象。
  const textFlattenStyle = StyleSheet.flatten<TextStyle>([
    styles.text,
    !!color && {color: plain ? color : 'white'},
    textStyle,
  ]);

  // 渲染图标
  const renderIcon = () => {
    const defaultIconSize = textFlattenStyle.fontSize;
    const iconColor = color ?? (textFlattenStyle.color as string);
    let marginStyles: ViewStyle;

    if (!text) {
      marginStyles = {};
    } else if (iconPosition === 'left') {
      marginStyles = {marginRight: 4};
    } else {
      marginStyles = {marginLeft: 4};
    }

    return (
      <>
        {icon && loading !== true && (
          <View style={marginStyles}>
            {/* React 提供的一个顶层 API,用于检查某个值是否为 React 元素 */}
            {React.isValidElement(icon)
              ? React.cloneElement(icon as React.ReactElement<any, string>, {
                  size: defaultIconSize,
                  color: iconColor,
                })
              : icon}
          </View>
        )}
        {loading && (
          <Loading
            // ?? 可选链操作符,如果 loadingSize 为 null 或 undefined ,就使用 defaultIconSize 作为默认值
            size={loadingSize ?? defaultIconSize}
            type={loadingType}
            color={iconColor}
            style={marginStyles}
          />
        )}
      </>
    );
  };
  // 渲染文本
  const renderText = () => {
    if (!text) {
      return null;
    }

    return (
      <Text selectable={false} numberOfLines={1} style={textFlattenStyle}>
        {text}
      </Text>
    );
  };

  return (
    <TouchableOpacity
      {...rest}
      disabled={disabled}
      activeOpacity={0.6}
      style={[
        styles.button,
        props.style,
        plain && styles.plain,
        round && styles.round,
        square && styles.square,
        disabled && styles.disabled,
        // !!是一种类型转换的方法,它可以将一个值转换为布尔类型的true或false
        !!color && {borderColor: color},
        !!color && !plain && {backgroundColor: color},
      ]}>
      {iconPosition === 'left' && renderIcon()}
      {renderText()}
      {iconPosition === 'right' && renderIcon()}
    </TouchableOpacity>
  );
});

export default Button;

4、对外导出 Botton 组件及其类型文件

import Button from './Button';

export default Button;
export {Button};
export type {ButtonProps, ButtonSize, ButtonType} from './type';

5、主题样式

动态生成样式对象调用函数

import {useMemo} from 'react';
import {createTheming} from '@callstack/react-theme-provider';
import type {StyleSheet} from 'react-native';
import {defaultTheme} from '../styles';
// 创建主题对象:调用 createTheming 函数并传入一个默认主题作为参数
export const {ThemeProvider, withTheme, useTheme} = createTheming<DiceUI.Theme>(
  defaultTheme as DiceUI.Theme,
);

type ThemeFactoryCallBack<T extends StyleSheet.NamedStyles<T>> = {
  styles: T;
  theme: DiceUI.Theme;
};

export function useThemeFactory<T extends StyleSheet.NamedStyles<T>, P>(
  fun: (theme: DiceUI.Theme, ...extra: P[]) => T,
  ...params: P[]
): ThemeFactoryCallBack<T> {
  // 钩子,用于在函数组件中获取当前的主题
  const theme = useTheme();
  const styles = useMemo(() => fun(theme, ...params), [fun, theme, params]);

  return {styles, theme};
}

export default {
  ThemeProvider,
  withTheme,
  useTheme,
  useThemeFactory,
};

6、Demo 演示

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/734152.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Python web 开发 flask 实践

1、前言 前文已经介绍了很多关于 python 的算法和脚本的写法&#xff0c;在本文将开启python的 web 的开发&#xff0c;和java 类似的&#xff0c;对于 web 开发也需要引入框架&#xff0c;对于 python 的 web 开发来说常见的有 flask 和 django 两种&#xff0c;在本文中将要…

通过阿里云OOS定时升级Redis实例临时带宽

功能背景 在数据驱动的现代业务环境中&#xff0c;Redis以其卓越的性能和灵活性&#xff0c;已成为众多企业关键基础设施的重要组成部分。Redis广泛应用于处理缓存、会话管理、消息队列等多种数据密集型和响应敏感型的场景&#xff0c;对业务连续性和用户体验贡献极大。然而&a…

transdreamer 论文阅读笔记

这篇文章是对dreamer系列的改进&#xff0c;是一篇world model 的论文改进点在于&#xff0c;dreamer用的是循环神经网络&#xff0c;本文想把它改成transformer&#xff0c;并且希望能利用transformer实现并行训练。改成transformer的话有个地方要改掉&#xff0c;dreamer用ht…

OpenCV Mat实现图像四则运算及常用四则运算的API函数

装载有图像数据的OpenCV Mat对象&#xff0c;可以说是一个图像矩阵&#xff0c;可以进行加、减、乘、除运算。特别是加运算特别有用。 一 与常数的四则运算 与常数的加运算 示例&#xff1a; #include <iostream> #include <opencv2/opencv.hpp>using namespace …

JVM 垃圾回收分配及算法

一、判断对象是否可以回收 垃圾收集器在做垃圾回收的时候&#xff0c;首先需要判定的就是哪些内存是需要被回收 的&#xff0c;哪些对象是「存活」的&#xff0c;是不可以被回收的&#xff1b;哪些对象已经「死掉」了&#xff0c;需 要被回收。 一般有两种方法来判断&#xff…

mysql数据库切换成kingbase(人大金仓)数据库时遇到的字段不存在问题

一、问题描述 mysql数据库切换成国产数据库人大金仓(kingbase)数据库的遇到的字段不存在的问题,根本原因其实是没有找到相对应的表,报错示例如下图所示: 二、问题解决 1、如果所有的表都发生上述的错误,kingbase的表在xml里面写sql的时候需要带上空间名的前缀,比如pu…

《Linux运维总结:prometheus+altermanager+webhook-dingtalk配置文件详解》

总结&#xff1a;整理不易&#xff0c;如果对你有帮助&#xff0c;可否点赞关注一下&#xff1f; 更多详细内容请参考&#xff1a;《Linux运维篇&#xff1a;Linux系统运维指南》 一、prometheus配置文件 Prometheus的配置文件是prometheus.yml&#xff0c;在启动时指定相关的…

09-axios在Vue中的导入与配置

09-axios 前言首先简单了解什么是Axios&#xff1f;以上完成后就可以使用了 前言 我们接着上一篇文章 08-路由地址的数据获取 来讲。 下一篇文章 10-vuex在Vue中的导入与配置 首先简单了解什么是Axios&#xff1f; Axios是一个基于Promise 用于浏览器和 nodejs 的 HTTP 客户端…

D触发器(D Flip-Flop)与D锁存器(D Latch)

1 基础概念 我们先来简单回顾一下D触发器&#xff08;D flip-flop&#xff09;和D锁存器&#xff08;D latch&#xff09;的概念&#xff0c;以及它们在数字电路中的作用。 1.1 D触发器&#xff08;D Flip-Flop&#xff09; D触发器是一种数字存储器件&#xff0c;它在时钟信号…

VBA学习(16):工作表事件示例:输入数据后锁定单元格

在工作表单元格中输入数据后&#xff0c;该单元格就被锁定&#xff0c;不能再编辑。 打开VBE&#xff0c;在工程资源管理器中双击该工作表名称打开其代码模块&#xff0c;在其中输入下面的代码&#xff1a; 假设整个工作表的LockedFalse Private Sub Worksheet_Change(ByVal …

超大cvs文件导入MySQL

1 XXX.cvs 太大 使用cvs拆分HugeCSVSplitter_jb51工具进行拆分&#xff0c;Line Count 设置为1,000,000 注意&#xff1a;1 拆分后除第一个子cvs文件含有标题外&#xff0c;其他的子文档都不含有标题行&#xff1b; 2 后一个文档的第一行为前一个文档的…

【尚庭公寓SpringBoot + Vue 项目实战】用户管理(十五)

【尚庭公寓SpringBoot Vue 项目实战】用户管理&#xff08;十五&#xff09; 文章目录 【尚庭公寓SpringBoot Vue 项目实战】用户管理&#xff08;十五&#xff09;1、业务介绍2、接口实现2.1、根据条件分页查询用户列表2.2、根据ID更新用户状态 1、业务介绍 用户管理共包含两…

数据结构与算法-B(B-)树的简单实现

B(B-)树定义 B树&#xff08;或B-tree&#xff09;是一个在计算机科学中广泛使用的数据结构&#xff0c;它是一种自平衡的树&#xff0c;能够保持数据有序。 以下是B树的特性 每个节点最多右m个孩子&#xff0c;二叉树是B-树的特例&#xff0c;其有2个孩子。除了叶节点和根节点…

【Gradio】从 BigQuery 数据创建实时仪表板

Google BigQuery 是一个基于云的服务&#xff0c;用于处理非常大的数据集。它是一个无服务器且高度可扩展的数据仓库解决方案&#xff0c;使用户能够使用类 SQL 查询分析数据。 在本教程中&#xff0c;我们将向您展示如何在 Python 中查询 BigQuery 数据集&#xff0c;并使用 g…

IntelliJ IDE 插件开发 | (十)主题插件开发入门

系列文章 本系列文章已收录到专栏&#xff0c;交流群号&#xff1a;689220994&#xff0c;也可点击链接加入。 前言 在前面的章节中&#xff0c;我们介绍的都是功能性插件的开发内容&#xff0c;本文则会介绍一下主题类插件的开发方式。不过本文也只是带大家入个门&#xff…

书生·浦语大模型LagentAgentLego智能体应用搭建 第二期

文章目录 智能体概述智能体的定义智能体组成智能体范式 环境配置Lagent&#xff1a;轻量级智能体框架实战Lagent Web Demo用 Lagent 自定义工具 AgentLego&#xff1a;组装智能体“乐高”直接使用AgentLego作为智能体工具使用 用 AgentLego 自定义工具 智能体概述 智能体的定义…

aardio - 【库】lock 跨进程读写锁

import win.ui; /*DSG{{*/ var winform win.form(text"aardio form";right272;bottom203;topmost1) winform.add( button{cls"button";text"无锁演示";left27;top132;right120;bottom184;z2}; button2{cls"button";text"有锁演示…

邀请函 | 人大金仓邀您相聚第十三届中国国际国防电子展览会

盛夏六月 备受瞩目的 第十三届中国国际国防电子展览会 将于6月26日至28日 在北京国家会议中心盛大举办 作为数据库领域国家队 人大金仓 将携系列行业解决方案 和创新实践成果亮相 期待您莅临指导 ↓↓↓↓↓↓ CIDEX 2024 中国国际国防电子展览会&#xff08;简称CIDEX&#xf…

Linux环境搭建之CentOS7(包含静态IP配置)

&#x1f525; 本文由 程序喵正在路上 原创&#xff0c;CSDN首发&#xff01; &#x1f496; 系列专栏&#xff1a;虚拟机 &#x1f320; 首发时间&#xff1a;2024年6月22日 &#x1f98b; 欢迎关注&#x1f5b1;点赞&#x1f44d;收藏&#x1f31f;留言&#x1f43e; 安装VMw…

在scrapy中使用Selector提取数据

经院吉吉&#xff1a; 首先说明一下&#xff0c;在scrapy中使用选择器是基于Selector这个对象滴&#xff0c;selector对象在scrapy中通过XPATH或是CSS来提取数据的&#xff0c;我们可以自己创建selector对象&#xff0c;但在实际开发中我们不需要这样做&#xff0c;因为respons…