From 997c88ac6a6d227ca3744a7c560f065022243e69 Mon Sep 17 00:00:00 2001 From: Banlangen <928012047@qq.com> Date: Fri, 17 Sep 2021 12:50:41 +0800 Subject: [PATCH] Feat/lg917 (#7) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit feat: 添加popup组件 --- .../vantui-demo/src/pages/demo2/index.tsx | 34 ++- .../src/{ => components}/mixins/transition.ts | 34 +-- .../vantui/src/components/overlay/index.tsx | 19 +- .../vantui/src/components/popup/index.tsx | 70 +++-- packages/vantui/src/components/popup/wxs.ts | 1 - .../vantui/src/components/stepper/index.tsx | 262 ++++++++++++++++++ packages/vantui/src/components/stepper/wxs.ts | 18 ++ .../vantui/src/components/steps/index.tsx | 110 ++++++++ .../src/components/transition/index.tsx | 26 +- packages/vantui/src/index.ts | 2 + packages/vantui/types/icon.d.ts | 2 +- packages/vantui/types/index.d.ts | 2 + packages/vantui/types/mixins/transition.d.ts | 12 +- packages/vantui/types/popup.d.ts | 4 +- packages/vantui/types/stepper.d.ts | 34 +++ packages/vantui/types/steps.d.ts | 23 ++ 16 files changed, 559 insertions(+), 94 deletions(-) rename packages/vantui/src/{ => components}/mixins/transition.ts (84%) create mode 100644 packages/vantui/src/components/stepper/index.tsx create mode 100644 packages/vantui/src/components/stepper/wxs.ts create mode 100644 packages/vantui/src/components/steps/index.tsx create mode 100644 packages/vantui/types/stepper.d.ts create mode 100644 packages/vantui/types/steps.d.ts diff --git a/packages/vantui-demo/src/pages/demo2/index.tsx b/packages/vantui-demo/src/pages/demo2/index.tsx index 92f0ee5c9..b4280624c 100644 --- a/packages/vantui-demo/src/pages/demo2/index.tsx +++ b/packages/vantui-demo/src/pages/demo2/index.tsx @@ -1,7 +1,7 @@ import { View, Button } from '@tarojs/components' import { useEffect, useState } from 'react' import { useDidHide, useDidShow } from '@tarojs/taro' -import { Popup } from '@antmjs/vantui' +import { Popup, Steps, Stepper } from '@antmjs/vantui' import './index.less' @@ -24,7 +24,37 @@ export default function Index() { return ( - + + + setShow(false)}> 不是设计开发不贷款分不开的 不是设计开发不贷款分不开的 diff --git a/packages/vantui/src/mixins/transition.ts b/packages/vantui/src/components/mixins/transition.ts similarity index 84% rename from packages/vantui/src/mixins/transition.ts rename to packages/vantui/src/components/mixins/transition.ts index d20a6ddb4..024f53b28 100644 --- a/packages/vantui/src/mixins/transition.ts +++ b/packages/vantui/src/components/mixins/transition.ts @@ -1,6 +1,6 @@ import { useState, useEffect, useCallback, useRef } from 'react' -import { isObj } from './../components/common/validator' -import { TransitionProps } from './../../types/mixins/transition' +import { isObj } from './../../components/common/validator' +import { TransitionProps } from './../../../types/mixins/transition' const getClassNames = (name: string) => ({ enter: `van-${name}-enter van-${name}-enter-active enter-class enter-active-class`, 'enter-to': `van-${name}-enter-to van-${name}-enter-active enter-to-class enter-active-class`, @@ -11,12 +11,12 @@ export function useTransition({ show = false, duration = 300, name = 'fade', - beforeEnter, - beforeLeave, - afterEnter, - afterLeave, - enter, - leave, + onBeforeEnter, + onBeforeLeave, + onAfterEnter, + onAfterLeave, + onEnter, + onLeave, }: TransitionProps) { const transitionEnded = useRef(false) const status = useRef('') @@ -30,28 +30,28 @@ export function useTransition({ } transitionEnded.current = true if (status.current === 'enter') { - afterEnter?.() + onAfterEnter?.() } else { - afterLeave?.() + onAfterLeave?.() } if (!show && display) { // this.setData({ display: false }) setDisplay(false) } - }, [afterEnter, afterLeave, display, show]) + }, [display, onAfterEnter, onAfterLeave, show]) const _enter = useCallback(() => { // const { duration, name } = this.data const classNames = getClassNames(name) const currentDuration = isObj(duration) ? (duration as any).enter : duration status.current = 'enter' // this.$emit('before-enter') - beforeEnter?.() + onBeforeEnter?.() requestAnimationFrame(() => { if (status.current !== 'enter') { return } - enter?.() + onEnter?.() setInited(true) setDisplay(true) setClasses(classNames.enter) @@ -64,7 +64,7 @@ export function useTransition({ setClasses(classNames['enter-to']) }) }) - }, [beforeEnter, duration, enter, name]) + }, [duration, name, onBeforeEnter, onEnter]) const _leave = useCallback(() => { if (!display) { return @@ -73,13 +73,13 @@ export function useTransition({ const classNames = getClassNames(name) const currentDuration = isObj(duration) ? (duration as any).leave : duration status.current = 'leave' - beforeLeave?.() + onBeforeLeave?.() requestAnimationFrame(() => { if (status.current !== 'leave') { return } // this.$emit('leave') - leave?.() + onLeave?.() setClasses(classNames.leave) setCurrentDuration(currentDuration) @@ -92,7 +92,7 @@ export function useTransition({ setClasses(classNames['leave-to']) }) }) - }, [beforeLeave, display, duration, leave, name, onTransitionEnd]) + }, [display, duration, name, onBeforeLeave, onLeave, onTransitionEnd]) useEffect(() => { show ? _enter() : _leave() }, [_enter, _leave, show]) diff --git a/packages/vantui/src/components/overlay/index.tsx b/packages/vantui/src/components/overlay/index.tsx index 7e4ccecda..c94382a10 100644 --- a/packages/vantui/src/components/overlay/index.tsx +++ b/packages/vantui/src/components/overlay/index.tsx @@ -11,26 +11,12 @@ export default function Index(props: OverlayProps) { lockScroll = true, duration = 300, children, - onClick, + ...others } = props const noop = useCallback((event) => { event.stopPropagation() event.preventDefault() }, []) - // className={ - // computed.rootClass({ - // classPrefix, - // name, - // }) + ` ${className}` - // } - // style={utils.style([ - // computed.rootStyle({ - // customStyle, - // color, - // size, - // }), - // style, - // ])} return lockScroll ? ( {children} @@ -47,7 +34,7 @@ export default function Index(props: OverlayProps) { className={'van-overlay' + ` ${className}`} style={utils.style([{ 'z-index': zIndex }, style])} duration={duration} - onClick={(e) => onClick?.(e)} + {...others} > {children} diff --git a/packages/vantui/src/components/popup/index.tsx b/packages/vantui/src/components/popup/index.tsx index 0f3f9286e..6224a1a14 100644 --- a/packages/vantui/src/components/popup/index.tsx +++ b/packages/vantui/src/components/popup/index.tsx @@ -5,14 +5,13 @@ import * as utils from '../wxs/utils' import { PopupProps } from '../../../types/popup' import VanIcon from './../icon' import * as computed from './wxs' -import { useTransition } from './../../mixins/transition' +import { useTransition } from './../mixins/transition' import VanOverlay from './../overlay' export default function Index(this: any, props: PopupProps) { const { show, - duration, - name, + duration = 300, round, closeable, overlayStyle, @@ -27,30 +26,30 @@ export default function Index(this: any, props: PopupProps) { safeAreaInsetTop = false, lockScroll = true, children, - clickOverlay, - beforeEnter, - beforeLeave, - afterEnter, - afterLeave, - enter, - leave, - close, + onClickOverlay, + onBeforeEnter, + onBeforeLeave, + onAfterEnter, + onAfterLeave, + onEnter, + onLeave, + onClose, style, className, ...others } = props const onClickCloseIcon = useCallback(() => { - close?.() - }, [close]) - const onClickOverlay = useCallback(() => { - clickOverlay?.() + onClose?.() + }, [onClose]) + const _onClickOverlay = useCallback(() => { + onClickOverlay?.() if (closeOnClickOverlay) { - close?.() + onClose?.() } - }, [clickOverlay, close, closeOnClickOverlay]) + }, [closeOnClickOverlay, onClickOverlay, onClose]) - const [_name, setName] = useState(name) - const [_duration, setDuration] = useState(0) + const [_name, setName] = useState('') + const [_duration, setDuration] = useState(duration) const originDuration = useRef(null) useEffect(() => { @@ -62,18 +61,17 @@ export default function Index(this: any, props: PopupProps) { setDuration(originDuration.current) } }, [duration, position, transition]) - const { inited, currentDuration, classes, display, onTransitionEnd } = useTransition({ show, duration: _duration, name: _name, - beforeEnter, - beforeLeave, - afterEnter, - afterLeave, - enter, - leave, + onBeforeEnter, + onBeforeLeave, + onAfterEnter, + onAfterLeave, + onEnter, + onLeave, }) // observeShow(value, old) { @@ -91,7 +89,7 @@ export default function Index(this: any, props: PopupProps) { zIndex={zIndex} style={overlayStyle} duration={duration} - onClick={onClickOverlay} + onClick={_onClickOverlay} lockScroll={lockScroll} /> )} @@ -101,8 +99,6 @@ export default function Index(this: any, props: PopupProps) { 'custom-class ' + classes + ' ' + - className + - ' ' + utils.bem('popup', [ position, { @@ -110,14 +106,17 @@ export default function Index(this: any, props: PopupProps) { safe: safeAreaInsetBottom, safeTop: safeAreaInsetTop, }, - ]) + ]) + + ` ${className}` } - style={computed.popupStyle({ - zIndex, - currentDuration, - display, + style={utils.style([ + computed.popupStyle({ + zIndex, + currentDuration, + display, + }), style, - })} + ])} onTransitionEnd={onTransitionEnd} {...others} > @@ -131,7 +130,6 @@ export default function Index(this: any, props: PopupProps) { closeIconPosition } onClick={onClickCloseIcon} - info={null} > )} diff --git a/packages/vantui/src/components/popup/wxs.ts b/packages/vantui/src/components/popup/wxs.ts index b47b9fbac..bf35d4894 100644 --- a/packages/vantui/src/components/popup/wxs.ts +++ b/packages/vantui/src/components/popup/wxs.ts @@ -9,7 +9,6 @@ function popupStyle(data: any) { 'transition-duration': data.currentDuration + 'ms', }, data.display ? null : 'display: none', - data.customStyle, ]) } export { popupStyle } diff --git a/packages/vantui/src/components/stepper/index.tsx b/packages/vantui/src/components/stepper/index.tsx new file mode 100644 index 000000000..0ecdb57b1 --- /dev/null +++ b/packages/vantui/src/components/stepper/index.tsx @@ -0,0 +1,262 @@ +import { View, Input } from '@tarojs/components' +import { useCallback, useEffect, useState, useRef } from 'react' +import * as utils from '../wxs/utils' +import { isDef } from '../common/validator.js' +import { StepperProps } from './../../../types/stepper' +import * as computed from './wxs' +const LONG_PRESS_START_TIME = 600 +const LONG_PRESS_INTERVAL = 200 +// add num and avoid float number +function add(num1: number, num2: number) { + const cardinal = Math.pow(10, 10) + return Math.round((num1 + num2) * cardinal) / cardinal +} +function equal(value1: any, value2: any) { + return String(value1) === String(value2) +} + +export default function Index(props: StepperProps) { + const { + theme, + value, + integer = 'check', + disabled, + inputWidth, + buttonSize, + asyncChange, + disableInput, + decimalLength, + min = 1, + max = Number.MAX_SAFE_INTEGER, + step = 1, + showPlus = true, + showMinus = true, + disablePlus, + disableMinus, + longPress = true, + className, + onFocus, + onChange, + onBlur, + onOverlimit, + onPlus, + renderMinus, + renderPlus, + ...others + } = props + const [currentValue, setCurrentValue] = useState(undefined) + const eventType = useRef('') + const longPressTimer = useRef(0) + const isLongPress = useRef(false) + // filter illegal characters + const filter = useCallback( + (value) => { + value = String(value).replace(/[^0-9.-]/g, '') + if (integer && value.indexOf('.') !== -1) { + value = value.split('.')[0] + } + return value + }, + [integer], + ) + // limit value range + const format = useCallback( + (value) => { + value = filter(value) + // format range + value = value === '' ? 0 : +value + value = Math.max(Math.min(max, value), min) + // format decimal + if (isDef(decimalLength)) { + value = value.toFixed(decimalLength) + } + return value + }, + [decimalLength, filter, max, min], + ) + + const check = useCallback(() => { + const val = format(currentValue) + if (!equal(val, currentValue)) { + setCurrentValue(val) + } + }, [currentValue, format]) + const isDisabled = useCallback( + (type) => { + if (type === 'plus') { + return disabled || disablePlus || currentValue >= max + } + return disabled || disableMinus || currentValue <= min + }, + [currentValue, disableMinus, disablePlus, disabled, max, min], + ) + const emitChange = useCallback( + (value) => { + if (!asyncChange) { + setCurrentValue(value) + } + onChange?.(value) + }, + [asyncChange, onChange], + ) + const _onInput = useCallback( + (event) => { + const { value = '' } = event.detail || {} + // allow input to be empty + if (value === '') { + return + } + let formatted = filter(value) + // limit max decimal length + if (isDef(decimalLength) && formatted.indexOf('.') !== -1) { + const pair = formatted.split('.') + formatted = `${pair[0]}.${pair[1].slice(0, decimalLength)}` + } + emitChange(formatted) + }, + [decimalLength, emitChange, filter], + ) + + const _onFocus = useCallback( + (event) => { + onFocus?.(event.detail) + }, + [onFocus], + ) + const _onBlur = useCallback( + (event) => { + const value = format(event.detail.value) + emitChange(value) + onBlur?.(Object.assign(Object.assign({}, event.detail), { value })) + }, + [emitChange, format, onBlur], + ) + const _onChange = useCallback(() => { + if (isDisabled(eventType.current)) { + onOverlimit?.(eventType.current) + return + } + const diff = eventType.current === 'minus' ? -step : +step + const value = format(add(+currentValue, diff)) + emitChange(value) + if (eventType.current === 'plus') { + onPlus?.() + } + // type?.() + }, [currentValue, emitChange, format, isDisabled, onOverlimit, onPlus, step]) + const longPressStep = useCallback(() => { + longPressTimer.current = setTimeout(() => { + _onChange() + longPressStep() + }, LONG_PRESS_INTERVAL) + }, [_onChange]) + const _onTap = useCallback( + (event) => { + const { type } = event.currentTarget.dataset + eventType.current = type + _onChange() + }, + [_onChange], + ) + const _onTouchStart = useCallback( + (event) => { + if (!longPress) { + return + } + clearTimeout(longPressTimer.current) + const { type } = event.currentTarget.dataset + eventType.current = type + isLongPress.current = false + longPressTimer.current = setTimeout(() => { + isLongPress.current = true + _onChange() + longPressStep() + }, LONG_PRESS_START_TIME) + }, + [longPress, longPressStep, _onChange], + ) + const _onTouchEnd = useCallback(() => { + if (!longPress) { + return + } + clearTimeout(longPressTimer.current) + }, [longPress]) + useEffect(() => { + if (!equal(value, currentValue)) { + setCurrentValue(format(value)) + } + }, [format, value]) + useEffect(() => { + check() + }, [decimalLength, min, max, integer, check]) + return ( + + {showMinus && ( + + {renderMinus} + + )} + + {showPlus && ( + = max, + }) + } + hoverClass="van-stepper__plus--hover" + // hoverStayTime="70" + onClick={_onTap} + onTouchStart={_onTouchStart} + onTouchEnd={_onTouchEnd} + > + {renderPlus} + + )} + + ) +} diff --git a/packages/vantui/src/components/stepper/wxs.ts b/packages/vantui/src/components/stepper/wxs.ts new file mode 100644 index 000000000..242278a5a --- /dev/null +++ b/packages/vantui/src/components/stepper/wxs.ts @@ -0,0 +1,18 @@ +import { style } from '../wxs/style' +import { addUnit } from '../wxs/add-unit' + +function buttonStyle(data: any) { + return style({ + width: addUnit(data.buttonSize), + height: addUnit(data.buttonSize), + }) +} + +function inputStyle(data: any) { + return style({ + width: addUnit(data.inputWidth), + height: addUnit(data.buttonSize), + }) +} + +export { inputStyle, buttonStyle } diff --git a/packages/vantui/src/components/steps/index.tsx b/packages/vantui/src/components/steps/index.tsx new file mode 100644 index 000000000..3723a1c0e --- /dev/null +++ b/packages/vantui/src/components/steps/index.tsx @@ -0,0 +1,110 @@ +import { View } from '@tarojs/components' +import * as utils from '../wxs/utils' +import { GREEN, GRAY_DARK } from '../common/color.js' +import VanIcon from '../icon/index' +import { StepsProps } from './../../../types/steps' +export function getStatus(index: number, active: any) { + if (index < active) { + return 'finish' + } else if (index === active) { + return 'process' + } + + return 'inactive' +} + +export default function Index(props: StepsProps) { + const { + steps = [], + active, + direction = 'horizontal', + activeColor = GREEN, + inactiveColor = GRAY_DARK, + activeIcon = 'checked', + inactiveIcon, + className, + onClick, + ...others + } = props + return ( + + + {steps.map((item, index) => { + return ( + + + {item.text} + {item.desc} + + + {index !== active ? ( + <> + {item.inactiveIcon || inactiveIcon ? ( + + ) : ( + + )} + + ) : ( + + )} + + {index !== steps.length - 1 && ( + + )} + + ) + })} + + + ) +} diff --git a/packages/vantui/src/components/transition/index.tsx b/packages/vantui/src/components/transition/index.tsx index d287ba9f2..2cac3220f 100644 --- a/packages/vantui/src/components/transition/index.tsx +++ b/packages/vantui/src/components/transition/index.tsx @@ -2,15 +2,15 @@ import { View } from '@tarojs/components' import { TransitionPropsCom } from '../../../types/transition' import * as utils from '../wxs/utils' import * as computed from './wxs' -import { useTransition } from './../../mixins/transition' +import { useTransition } from './../mixins/transition' export default function Index(props: TransitionPropsCom) { const { - beforeEnter, - beforeLeave, - afterEnter, - afterLeave, - enter, - leave, + onBeforeEnter, + onBeforeLeave, + onAfterEnter, + onAfterLeave, + onEnter, + onLeave, duration, name, show, @@ -23,12 +23,12 @@ export default function Index(props: TransitionPropsCom) { show, duration: duration, name: name, - beforeEnter, - beforeLeave, - afterEnter, - afterLeave, - enter, - leave, + onBeforeEnter, + onBeforeLeave, + onAfterEnter, + onAfterLeave, + onEnter, + onLeave, }) return ( diff --git a/packages/vantui/src/index.ts b/packages/vantui/src/index.ts index 6695a9a0c..0047cb987 100644 --- a/packages/vantui/src/index.ts +++ b/packages/vantui/src/index.ts @@ -19,5 +19,7 @@ export { default as Search } from './components/search' export { default as Skeleton } from './components/skeleton' export { default as Tag } from './components/tag' export { default as CountDown } from './components/count-down' +export { default as Stepper } from './components/stepper' +export { default as Steps } from './components/steps' export { default as Tabbar } from './components/tabbar' export { default as TabbarItem } from './components/tabbar-item' diff --git a/packages/vantui/types/icon.d.ts b/packages/vantui/types/icon.d.ts index 10130935b..6a468f60b 100644 --- a/packages/vantui/types/icon.d.ts +++ b/packages/vantui/types/icon.d.ts @@ -3,7 +3,7 @@ import { StandardProps } from '@tarojs/components' export interface IconProps extends StandardProps { dot?: boolean - info: null | string + info?: null | string size?: number | string color?: string customStyle?: string diff --git a/packages/vantui/types/index.d.ts b/packages/vantui/types/index.d.ts index 8007424ee..a79acb2c7 100644 --- a/packages/vantui/types/index.d.ts +++ b/packages/vantui/types/index.d.ts @@ -19,5 +19,7 @@ export { Search } from './search.d' export { Skeleton } from './skeleton.d' export { CountDown } from './count-down.d' export { Tag } from './tag.d' +export { Stepper } from './stepper.d' +export { Steps } from './steps.d' export { Tabbar } from './tabbar.d' export { TabbarItem } from './tabbar-item.d' diff --git a/packages/vantui/types/mixins/transition.d.ts b/packages/vantui/types/mixins/transition.d.ts index 185a92432..0bf85a078 100644 --- a/packages/vantui/types/mixins/transition.d.ts +++ b/packages/vantui/types/mixins/transition.d.ts @@ -2,10 +2,10 @@ export interface TransitionProps { show?: boolean duration?: number | { enter: number; leave: number } name?: string - beforeEnter?: (...arg: any[]) => any - beforeLeave?: (...arg: any[]) => any - afterEnter?: (...arg: any[]) => any - afterLeave?: (...arg: any[]) => any - enter?: (...arg: any[]) => any - leave?: (...arg: any[]) => any + onBeforeEnter?: (...arg: any[]) => any + onBeforeLeave?: (...arg: any[]) => any + onAfterEnter?: (...arg: any[]) => any + onAfterLeave?: (...arg: any[]) => any + onEnter?: (...arg: any[]) => any + onLeave?: (...arg: any[]) => any } diff --git a/packages/vantui/types/popup.d.ts b/packages/vantui/types/popup.d.ts index 413809a91..f41c9d522 100644 --- a/packages/vantui/types/popup.d.ts +++ b/packages/vantui/types/popup.d.ts @@ -20,8 +20,8 @@ export interface PopupProps safeAreaInsetBottom?: boolean safeAreaInsetTop?: boolean children?: JSX.Element | JSX.Element[] | string - clickOverlay?: (...arg: any[]) => any - close?: (...arg: any[]) => any + onClickOverlay?: (...arg: any[]) => any + onClose?: (...arg: any[]) => any } declare const Popup: ComponentClass diff --git a/packages/vantui/types/stepper.d.ts b/packages/vantui/types/stepper.d.ts new file mode 100644 index 000000000..0e719b58d --- /dev/null +++ b/packages/vantui/types/stepper.d.ts @@ -0,0 +1,34 @@ +import { ComponentClass } from 'react' +import { StandardProps } from '@tarojs/components' + +export interface StepperProps extends StandardProps { + value?: number + integer?: boolean + disabled?: boolean + inputWidth?: string + buttonSize?: string + asyncChange?: boolean + disableInput?: boolean + decimalLength?: number + min?: number + max?: number + step?: number + showPlus?: boolean + showMinus?: boolean + disablePlus?: boolean + disableMinus?: boolean + longPress?: boolean + theme?: string + onFocus?: (...arg: any[]) => any + onChange?: (...arg: any[]) => any + onBlur?: (...arg: any[]) => any + onOverlimit?: (...arg: any[]) => any + onPlus?: (...arg: any[]) => any + onMinus?: (...arg: any[]) => any + renderMinus?: JSX.Element | JSX.Element[] | string + renderPlus?: JSX.Element | JSX.Element[] | string +} +// Partial +declare const Stepper: ComponentClass + +export { Stepper } diff --git a/packages/vantui/types/steps.d.ts b/packages/vantui/types/steps.d.ts new file mode 100644 index 000000000..50a3f161b --- /dev/null +++ b/packages/vantui/types/steps.d.ts @@ -0,0 +1,23 @@ +import { ComponentClass } from 'react' +import { StandardProps } from '@tarojs/components' + +export interface StepsProps extends StandardProps { + icon?: string + steps?: { + index?: number + desc: string + text: string + activeIcon?: string + inactiveIcon?: string + }[] + active?: number + direction?: string + activeColor?: string + inactiveColor?: string + activeIcon?: string + inactiveIcon?: string +} + +declare const Steps: ComponentClass + +export { Steps }