Initial commit: expo-popcore-app
This commit is contained in:
253
components/ui/Toast.tsx
Normal file
253
components/ui/Toast.tsx
Normal file
@@ -0,0 +1,253 @@
|
||||
import React, { ReactNode } from 'react'
|
||||
import Text from './Text'
|
||||
import Block from './Block'
|
||||
|
||||
import { ActivityIndicator } from 'react-native'
|
||||
|
||||
// 定义insets接口
|
||||
interface Insets {
|
||||
safeAreaInsetsBottom: number
|
||||
safeAreaInsetsTop: number
|
||||
safeAreaInsetsLeft: number
|
||||
safeAreaInsetsRight: number
|
||||
}
|
||||
|
||||
// 定义全局变量接口
|
||||
declare global {
|
||||
interface Window {
|
||||
toast?: {
|
||||
show: (component: ReactNode, config?: any) => void
|
||||
hide: () => void
|
||||
}
|
||||
loading?: {
|
||||
show: (component: ReactNode, config?: any) => void
|
||||
hide: () => void
|
||||
}
|
||||
modal?: {
|
||||
show: (component: ReactNode, config?: any) => void
|
||||
hide: () => void
|
||||
}
|
||||
actionSheet?: {
|
||||
show: (component: ReactNode, config?: any) => void
|
||||
hide: () => void
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 参数接口定义
|
||||
interface ShowParams {
|
||||
renderContent?: () => ReactNode
|
||||
title?: string
|
||||
duration?: number
|
||||
hideBackdrop?: boolean
|
||||
}
|
||||
|
||||
interface ShowActionSheetParams {
|
||||
itemList?: string[]
|
||||
renderContent?: () => ReactNode
|
||||
}
|
||||
|
||||
const isString = (variable: any): variable is string =>
|
||||
Object.prototype.toString.call(variable) === '[object String]'
|
||||
|
||||
const Toast = (function () {
|
||||
// 初始化默认insets
|
||||
let insets: Insets = {
|
||||
safeAreaInsetsBottom: 0,
|
||||
safeAreaInsetsTop: 0,
|
||||
safeAreaInsetsLeft: 0,
|
||||
safeAreaInsetsRight: 0,
|
||||
}
|
||||
|
||||
// // 使用回调函数获取insets
|
||||
// StaticSafeAreaInsets.getSafeAreaInsets((staticInsets) => {
|
||||
// insets = staticInsets
|
||||
// })
|
||||
|
||||
let toastTimer: number | null = null
|
||||
const show = (params?: ShowParams): void => {
|
||||
const { renderContent, title, duration = 2000, hideBackdrop = true } = params || {}
|
||||
hide()
|
||||
|
||||
const renderBody = (): ReactNode => {
|
||||
if (renderContent) {
|
||||
return renderContent()
|
||||
}
|
||||
return title && <Text className="text-[16px] text-white">{title}</Text>
|
||||
}
|
||||
|
||||
; (global as any).toast?.show(
|
||||
<Block className="z-[9999] flex items-center justify-center">
|
||||
<Block className="relative mx-[20px] mt-[-40px] rounded-[2px] bg-black/85 px-[12px] py-[8px]">
|
||||
{renderBody()}
|
||||
</Block>
|
||||
</Block>,
|
||||
{
|
||||
style: {},
|
||||
swipeDirection: null,
|
||||
hideBackdrop,
|
||||
onBackdropPress: () => { },
|
||||
entering: false,
|
||||
exiting: false,
|
||||
},
|
||||
)
|
||||
|
||||
if (duration > 0) {
|
||||
toastTimer = setTimeout(() => {
|
||||
hide()
|
||||
}, duration)
|
||||
}
|
||||
}
|
||||
|
||||
const hide = (): void => {
|
||||
; (global as any).toast?.hide()
|
||||
if (toastTimer) {
|
||||
clearTimeout(toastTimer)
|
||||
toastTimer = null
|
||||
}
|
||||
}
|
||||
|
||||
// loading
|
||||
let loadingTimer: number | null = null
|
||||
const showLoading = (params?: ShowParams): void => {
|
||||
const { renderContent, title, duration = 1500, hideBackdrop = false } = params || {}
|
||||
hideLoading()
|
||||
|
||||
const renderBody = (): ReactNode => {
|
||||
if (renderContent) {
|
||||
return renderContent()
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<ActivityIndicator size="small" />
|
||||
{!!title && (
|
||||
<Block className="mt-[12px] items-center px-[10px]">
|
||||
<Text className="text-[14px] text-white">{title}</Text>
|
||||
</Block>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
; (global as any).loading?.show(
|
||||
<Block className="z-[9999] flex items-center justify-center">
|
||||
<Block className="mt-[-40px] min-h-[100px] min-w-[100px] items-center justify-center overflow-hidden rounded-[8px] bg-black/85 py-[20px]">
|
||||
{renderBody()}
|
||||
</Block>
|
||||
</Block>,
|
||||
{
|
||||
style: {},
|
||||
swipeDirection: null,
|
||||
hideBackdrop,
|
||||
backdropColor: 'rgba(0,0,0,0.1)',
|
||||
onBackdropPress: () => { },
|
||||
entering: false,
|
||||
exiting: false,
|
||||
},
|
||||
)
|
||||
|
||||
if (duration > 0) {
|
||||
loadingTimer = setTimeout(() => {
|
||||
hideLoading()
|
||||
}, duration)
|
||||
}
|
||||
}
|
||||
|
||||
const hideLoading = (): void => {
|
||||
; (global as any).loading?.hide()
|
||||
if (loadingTimer) {
|
||||
clearTimeout(loadingTimer)
|
||||
loadingTimer = null
|
||||
}
|
||||
}
|
||||
|
||||
// modal
|
||||
const showModal = (ele: ReactNode, config: any = {}): void => {
|
||||
; (global as any).modal?.show(ele, {
|
||||
swipeDirection: null,
|
||||
...config,
|
||||
})
|
||||
}
|
||||
|
||||
const hideModal = (): void => {
|
||||
; (global as any).modal?.hide()
|
||||
}
|
||||
|
||||
// actionsheet
|
||||
// 兼容半屏组件
|
||||
const showActionSheet = ({ itemList, renderContent }: ShowActionSheetParams): Promise<number> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const handleSelect = (item: string): void => {
|
||||
hideActionSheet()
|
||||
const itemIndex = itemList?.findIndex((i) => i === item) || -1
|
||||
resolve(itemIndex)
|
||||
}
|
||||
|
||||
const handleCancel = (): void => {
|
||||
hideActionSheet()
|
||||
reject()
|
||||
}
|
||||
|
||||
const renderBody = (): ReactNode => {
|
||||
if (Array.isArray(itemList)) {
|
||||
return (
|
||||
<Block className="bg-white2 mb-[-1000px] gap-[1px] overflow-hidden rounded-t-[12px]">
|
||||
{itemList.map((item, index) => (
|
||||
<Block
|
||||
onClick={() => handleSelect(item)}
|
||||
key={index}
|
||||
className="items-center justify-center bg-white py-[16px]"
|
||||
>
|
||||
<Text className="text-[16px] text-black">{item}</Text>
|
||||
</Block>
|
||||
))}
|
||||
<Block className="bg-white2 mt-[8px] w-full" />
|
||||
<Block
|
||||
onClick={handleCancel}
|
||||
style={{ paddingBottom: insets.safeAreaInsetsBottom + 20 + 1000 }}
|
||||
className="flex items-center justify-center bg-white py-[16px]"
|
||||
>
|
||||
<Text className="text-[16px] text-black">取消</Text>
|
||||
</Block>
|
||||
</Block>
|
||||
)
|
||||
}
|
||||
|
||||
if (!renderContent) return null
|
||||
|
||||
return (
|
||||
<Block className="">
|
||||
{/* <ModalHeader /> */}
|
||||
{/* 安卓闪动,向上2px */}
|
||||
<Block className="mt-[-2px]">{renderContent()}</Block>
|
||||
<Block
|
||||
className="mb-[-1000px] w-full bg-white"
|
||||
style={{ height: insets.safeAreaInsetsBottom + 20 + 1000, position: 'relative' }}
|
||||
/>
|
||||
</Block>
|
||||
)
|
||||
}
|
||||
|
||||
; (global as any).actionSheet?.show(<Block className="">{renderBody()}</Block>, {
|
||||
style: { justifyContent: 'flex-end' },
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const hideActionSheet = (): void => {
|
||||
; (global as any).actionSheet?.hide()
|
||||
}
|
||||
|
||||
return {
|
||||
showActionSheet,
|
||||
hideActionSheet,
|
||||
showModal,
|
||||
hideModal,
|
||||
showLoading,
|
||||
hideLoading,
|
||||
show,
|
||||
hide,
|
||||
}
|
||||
})()
|
||||
|
||||
export default Toast
|
||||
Reference in New Issue
Block a user