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}
)
}