import React, { ReactNode, useRef, useMemo, useEffect, Fragment as Blk } from 'react' import Taro from '@tarojs/taro' import { Swiper as Swi, SwiperItem as SwiI, Button as Btn, Switch as Sch, ScrollView as ScV, } from '@tarojs/components-rn' import { ActivityIndicator, TouchableOpacity, Keyboard, TextInput, Platform, Text as Txt, View as Viw, } from 'react-native' import FastImage from 'react-native-fast-image' import {RTX} from './constants/constants' import {changeS4, changeS2} from './constants/utils' const sys = Taro.getSystemInfoSync() export type TFontSize = string export type TFontWeight = 'normal' | 'bold' export type TFontSpacing = string export type TOverflow = 'visible' | 'hidden' | 'scroll' export type TDisplay = 'flex' | 'block' | 'inline-block' | 'none' export type TFlexWay = 'fraa' | 'frab' | 'frac' | 'frae' | 'fras' | 'frba' | 'frbb' | 'frbc' | 'frbe' | 'frbs' | 'frca' | 'frcb' | 'frcc' | 'frce' | 'frcs' | 'frea' | 'freb' | 'frec' | 'free' | 'fres' | 'frsa' | 'frsb' | 'frsc' | 'frse' | 'frss' | 'fcaa' | 'fcab' | 'fcac' | 'fcae' | 'fcas' | 'fcba' | 'fcbb' | 'fcbc' | 'fcbe' | 'fcbs' | 'fcca' | 'fccb' | 'fccc' | 'fcce' | 'fccs' | 'fcea' | 'fceb' | 'fcec' | 'fcee' | 'fces' | 'fcsa' | 'fcsb' | 'fcsc' | 'fcse' | 'fcss' export type TBoxSizing = 'content-box' | 'border-box' export type TPosition = 'absolute' | 'relative' export type TBgImageType = 'aspectFit' | 'aspectFill' export type TSafeType = 'top' | 'bottom' export type TTextDecoration = 'underline' | 'none' | 'line-through' export type TFlexWrap = 'nowrap' | 'wrap' export type TFontStyle = 'normal' | 'italic' export type TPointerEvents = 'auto' | 'none' export type TStroke = string export type TLeft = string export type TRight = string export type TTop = string export type TBottom = string export type TPadding = string export type TMargin = string export type TSize = string export type TRadius = string export type TColor = string export type TZIndex = string export type TOpacity = string export type TBackDrop = string export type TMinHeight = string export type TMaxHeight = string export type THoverBgColor = string export type TBorderWidth = string export type TAnimation = Animate export type TJustifyContent = 'center' | 'space-between' | 'space-around' | 'flex-end' | 'flex-start' const keyboardTypeMap: any = { text: 'default', number: 'numeric', idcard: 'default', digit: Platform.select({ ios: 'decimal-pad', android: 'numeric' }) || '' } // 块 interface BlockProps{ children?: ReactNode } export const Block = (props: BlockProps) => ( {props.children} ) // 容器 interface BoxBase { children?: ReactNode size?: TSize radius?: TRadius overflow?: TOverflow bgColor?: TColor position?: TPosition left?: TLeft top?: TTop right?: TRight bottom?: TBottom borderColor?: TColor zIndex?: TZIndex opacity?: TOpacity minHeight?: TMinHeight maxHeight?: TMaxHeight pointerEvents?: TPointerEvents borderWidth?: TBorderWidth onClick?: () => void } interface BoxProps extends BoxBase{ display?: TDisplay padding?: TPadding margin?: TMargin flexWrap?: TFlexWrap justifyContent?: TJustifyContent } export const Box = (props: BoxProps) => { return ( {props.children} ) } interface SafeAreaProps extends BoxBase{ safe: TSafeType display?: TDisplay flexWrap?: TFlexWrap justifyContent?: TJustifyContent } export const SafeArea = (props: SafeAreaProps) => { const height = useMemo(() => { let baseH: number = (props.size ? changeS2(props.size)[1] : 1) as number if(props.safe === 'top'){ return baseH + sys.statusBarHeight } if(props.safe === 'bottom'){ return baseH + sys.screenHeight - sys.safeArea.bottom } return baseH }, [props.size, props.safe]) return ( {props.children} ) } interface FlexProps extends BoxBase{ flex: TFlexWay padding?: TPadding margin?: TMargin } export const Flex = (props: FlexProps) => { return ( {props.children} ) } // 点击 interface PressProps { size?: TSize children?: ReactNode opacity?: THoverBgColor onClick?: Function onPressIn?: Function onPressOut?: Function onLongPress?: Function radius?: TRadius } export const Press = (props: PressProps) => { const {onClick=()=>{}, opacity='0.5', onPressIn=()=>{}, onPressOut=()=>{}, onLongPress=()=>{}} = props return ( { onClick(e) }} onLongPress={(e) => { onLongPress(e) }} onPressIn={(e) => { onPressIn(e) }} onPressOut={(e) => { onPressOut(e) }} > {props.children} ) } // 横线 interface LineProps { safe?: TSafeType bgColor?: TColor size?: string radius?: TRadius } export const Line = (props: LineProps) => { const height = useMemo(() => { let baseH: number = (props.size ? changeS2(props.size)[1] : 1) as number if(props.safe === 'top'){ return baseH + sys.statusBarHeight } if(props.safe === 'bottom'){ return baseH + sys.screenHeight - sys.safeArea.bottom } return baseH }, [props.size, props.safe]) return ( ) } // 字体 interface TextProps{ children?: ReactNode fontSize?: TFontSize color?: TColor fontWeight?: TFontWeight lineHeight?: string textDecoration?: TTextDecoration opacity?: TOpacity stroke?: TStroke fontStyle?: TFontStyle letterSpacing?: string } export const Text = (props: TextProps) => ( {props.children} ) interface TextEllipsisProps extends TextProps{ line?: string } export const TextEllipsis = (props: TextEllipsisProps) => ( {props.children} ) // 图片 interface ImageProps { size?: TSize padding?: TPadding margin?: TMargin radius?: TRadius src: any position?: TPosition left?: TLeft top?: TTop right?: TRight bottom?: TBottom zIndex?: TZIndex mode?: 'aspectFill' | 'aspectFit' | 'center' opacity?: TOpacity pointerEvents?: TPointerEvents } const IMG_TYPE = { 'aspectFill': FastImage.resizeMode.cover, 'aspectFit': FastImage.resizeMode.contain, 'center': FastImage.resizeMode.center, } export const Image = (props: ImageProps) => ( ) // Input interface InputBase { size?: TSize disabled?: boolean padding?: TPadding margin?: TMargin radius?: TRadius position?: TPosition left?: TLeft top?: TTop right?: TRight bottom?: TBottom fontSize?: TFontSize color?: TColor bgColor?: TColor pColor?: TColor fontWeight?: TFontWeight placeholder?: string onInput?: Function onFocus?: Function autoFocus?: boolean onBlur?: Function // onConfirm?: Function value?: string | number, maxlength?: string } interface InputProps extends InputBase{ password?: boolean type?: 'text' | 'number' | 'idcard' | 'digit' } export const Input = (props: InputProps) => { const inputRef = useRef(null) useEffect(() => { if(props.autoFocus){ inputRef.current?.focus() } }, [props.autoFocus]) useEffect(() => { const hideSubscription = Keyboard.addListener("keyboardDidHide", () => { inputRef.current?.blur() }) return () => { hideSubscription.remove(); } }, [inputRef.current]) return ( { props.onInput && props.onInput(text) }} value={props.value ? props.value.toString() : ''} onBlur={() => { props.onBlur && props.onBlur() }} onFocus={() => { props.onFocus && props.onFocus() }} placeholder={props.placeholder || '请输入内容'} placeholderTextColor={props.pColor || '#8c8c8c'} keyboardType={keyboardTypeMap[props.type || 'text']} maxLength={+(props.maxlength || '140') === -1 ? undefined : +(props.maxlength || '140')} style={{ width: props.size ? changeS2(props.size)[0] : '100%', height: props.size ? changeS2(props.size)[1] : 80 * RTX, paddingTop: changeS4(props.padding)[0] || 0, paddingRight: changeS4(props.padding)[1] || 0, paddingBottom: changeS4(props.padding)[2] || 0, paddingLeft: changeS4(props.padding)[3] || 0, marginTop: changeS4(props.margin)[0] || 0, marginRight: changeS4(props.margin)[1] || 0, marginBottom: changeS4(props.margin)[2] || 0, marginLeft: changeS4(props.margin)[3] || 0, borderTopLeftRadius: changeS4(props.radius)[0] || 0, borderTopRightRadius: changeS4(props.radius)[1] || 0, borderBottomRightRadius: changeS4(props.radius)[2] || 0, borderBottomLeftRadius: changeS4(props.radius)[2] || 0, backgroundColor: props.bgColor || 'rgba(0,0,0,0)', fontSize: props.fontSize ? +props.fontSize * RTX : 32 * RTX, color: props.color || '#181818', fontWeight: props.fontWeight || 'normal', }} /> ) } interface TextareaProps extends InputProps{ // autoHeight?: boolean } export const Textarea = (props: TextareaProps) => { const inputRef = useRef(null) useEffect(() => { const hideSubscription = Keyboard.addListener("keyboardDidHide", () => { inputRef.current?.blur() }) return () => { hideSubscription.remove(); } }, [inputRef.current]) return ( { props.onInput && props.onInput(text) }} value={props.value ? props.value.toString() : ''} placeholder={props.placeholder || '请输入内容'} placeholderTextColor={props.pColor || '#8c8c8c'} keyboardType={keyboardTypeMap[props.type || 'text']} maxLength={+(props.maxlength || '140') === -1 ? undefined : +(props.maxlength || '140')} // autoHeight={props.autoHeight || false} textAlignVertical='top' style={{ width: props.size ? changeS2(props.size)[0] : '100%', height: props.size ? changeS2(props.size)[1] : 168 * RTX, paddingTop: changeS4(props.padding)[0] || 0, paddingRight: changeS4(props.padding)[1] || 0, paddingBottom: changeS4(props.padding)[2] || 0, paddingLeft: changeS4(props.padding)[3] || 0, marginTop: changeS4(props.margin)[0] || 0, marginRight: changeS4(props.margin)[1] || 0, marginBottom: changeS4(props.margin)[2] || 0, marginLeft: changeS4(props.margin)[3] || 0, borderTopLeftRadius: changeS4(props.radius)[0] || 0, borderTopRightRadius: changeS4(props.radius)[1] || 0, borderBottomRightRadius: changeS4(props.radius)[2] || 0, borderBottomLeftRadius: changeS4(props.radius)[2] || 0, backgroundColor: props.bgColor || 'rgba(0,0,0,0)', fontSize: props.fontSize ? +props.fontSize * RTX : 32 * RTX, color: props.color || '#181818', fontWeight: props.fontWeight || 'normal', }} /> ) } /** 按钮 */ interface ButtonProps { loading?: boolean children?: ReactNode onClick?: Function fontSize?: TFontSize color?: TColor fontWeight?: TFontWeight isFixAnimate?: boolean disable?: boolean size?: TSize radius?: TRadius overflow?: TOverflow bgColor?: TColor borderColor?: TColor zIndex?: TZIndex opacity?: TOpacity margin?: TMargin } export const Button = (props: ButtonProps) => { const {onClick=() => {},loading=false} = props return ( { if(props.disable) return if(loading) return onClick(e) }} > { loading && } { typeof(props.children) === 'string' ? {props.children} : props.children } ) } interface SwiperProps{ size?: TSize childList: ReactNode[] onChange?: Function current?: number autoplay?: boolean circular?: boolean vertical?: boolean radius?: TRadius padding?: TPadding bgColor?: TColor indicatorDots?: boolean indicatorColor?: TColor indicatorActiveColor?: TColor } export const Swiper = (props: SwiperProps) => ( { if(props.onChange) props.onChange(e.detail.current) }} > { props.childList.map((item, index) => ( {item} )) } ) interface SwitchProps { disable?: boolean checked?: boolean bgColor?: string onChange?: (value: boolean) => void } export const Switch = (props: SwitchProps) => { const {onChange=()=>{}} = props return ( onChange(e.detail.value)} /> ) } interface ScrollViewProps { size?: TSize scrollX?: boolean scrollY?: boolean upperThreshold?: string | number lowerThreshold?: string | number scrollTop?: string | number scrollLeft?: string | number enableFlex?: boolean onScrollToUpper?: (event: any) => void onScrollToLower?: (event: any) => void onScroll?: (event: any) => void children?: ReactNode radius?: TRadius padding?: TPadding bgColor?: TColor scrollWithAnimation?: boolean showsVerticalScrollIndicator?: boolean showsHorizontalScrollIndicator?: boolean } export const ScrollView = (props: ScrollViewProps) => { const { scrollX, scrollY, upperThreshold='50', lowerThreshold='50', scrollTop, scrollLeft, enableFlex, onScrollToUpper=()=>{}, onScrollToLower=()=>{}, onScroll=()=>{}, children, scrollWithAnimation, showsVerticalScrollIndicator, showsHorizontalScrollIndicator } = props return ( {children} ) }