diff --git a/package.json b/package.json index e5d152d46..662c006f7 100644 --- a/package.json +++ b/package.json @@ -60,7 +60,7 @@ "@vant/icons": "^1.5.2", "@vant/lazyload": "^1.0.2", "@vant/popperjs": "^1.0.2", - "@vant/use": "^1.0.4" + "@vant/use": "^1.0.5" }, "peerDependencies": { "vue": "^3.0.0" diff --git a/src/swipe-item/index.js b/src/swipe-item/index.tsx similarity index 77% rename from src/swipe-item/index.js rename to src/swipe-item/index.tsx index 3d1e5334e..aa2262fb0 100644 --- a/src/swipe-item/index.js +++ b/src/swipe-item/index.tsx @@ -1,5 +1,5 @@ -import { computed, nextTick, onMounted, reactive } from 'vue'; -import { SWIPE_KEY } from '../swipe'; +import { computed, CSSProperties, nextTick, onMounted, reactive } from 'vue'; +import { SWIPE_KEY, SwipeProvide } from '../swipe'; import { createNamespace } from '../utils'; import { useParent } from '@vant/use'; import { useExpose } from '../composables/use-expose'; @@ -8,17 +8,24 @@ const [createComponent, bem] = createNamespace('swipe-item'); export default createComponent({ setup(props, { slots }) { - let rendered; + let rendered: boolean; const state = reactive({ offset: 0, inited: false, mounted: false, }); - const { parent, index } = useParent(SWIPE_KEY); + const { parent, index } = useParent(SWIPE_KEY); + + if (!parent) { + if (process.env.NODE_ENV !== 'production') { + console.error('[Vant] SwipeItem must be a child component of Swipe.'); + } + return; + } const style = computed(() => { - const style = {}; + const style: CSSProperties = {}; const { vertical } = parent.props; if (parent.size.value) { @@ -56,7 +63,7 @@ export default createComponent({ return rendered; }); - const setOffset = (offset) => { + const setOffset = (offset: number) => { state.offset = offset; }; diff --git a/src/swipe/index.js b/src/swipe/index.tsx similarity index 83% rename from src/swipe/index.js rename to src/swipe/index.tsx index c04b5fd25..5816f1c98 100644 --- a/src/swipe/index.js +++ b/src/swipe/index.tsx @@ -1,5 +1,6 @@ import { ref, + Ref, watch, reactive, computed, @@ -7,6 +8,8 @@ import { onActivated, onDeactivated, onBeforeUnmount, + CSSProperties, + ComponentPublicInstance, } from 'vue'; // Utils @@ -27,11 +30,25 @@ const [createComponent, bem] = createNamespace('swipe'); export const SWIPE_KEY = 'vanSwipe'; +export type SwipeToOptions = { + immediate?: boolean; +}; + +export type SwipeProvide = { + props: { + loop: boolean; + vertical?: boolean; + lazyRender?: boolean; + }; + size: Ref; + count: Ref; + activeIndicator: Ref; +}; + export default createComponent({ props: { width: [Number, String], height: [Number, String], - autoplay: [Number, String], vertical: Boolean, lazyRender: Boolean, indicatorColor: String, @@ -39,6 +56,10 @@ export default createComponent({ type: Boolean, default: true, }, + autoplay: { + type: [Number, String], + default: 0, + }, duration: { type: [Number, String], default: 500, @@ -64,9 +85,9 @@ export default createComponent({ emits: ['change'], setup(props, { emit, slots }) { - const root = ref(); + const root = ref(); const state = reactive({ - rect: null, + rect: null as DOMRect | null, width: 0, height: 0, offset: 0, @@ -76,7 +97,10 @@ export default createComponent({ const touch = useTouch(); const windowSize = useWindowSize(); - const { children, linkChildren } = useChildren(SWIPE_KEY); + const { children, linkChildren } = useChildren< + // eslint-disable-next-line + ComponentPublicInstance<{}, any> + >(SWIPE_KEY); const count = computed(() => children.length); @@ -86,11 +110,13 @@ export default createComponent({ props.vertical ? touch.deltaY.value : touch.deltaX.value ); - const minOffset = computed( - () => - (props.vertical ? state.rect.height : state.rect.width) - - size.value * count.value - ); + const minOffset = computed(() => { + if (state.rect) { + const base = props.vertical ? state.rect.height : state.rect.width; + return base - size.value * count.value; + } + return 0; + }); const maxCount = computed(() => Math.ceil(Math.abs(minOffset.value) / size.value) @@ -110,7 +136,7 @@ export default createComponent({ const trackStyle = computed(() => { const mainAxis = props.vertical ? 'height' : 'width'; const crossAxis = props.vertical ? 'width' : 'height'; - const style = { + const style: CSSProperties = { transitionDuration: `${state.swiping ? 0 : props.duration}ms`, transform: `translate${props.vertical ? 'Y' : 'X'}(${state.offset}px)`, }; @@ -123,7 +149,7 @@ export default createComponent({ return style; }); - const getTargetActive = (pace) => { + const getTargetActive = (pace: number) => { const { active } = state; if (pace) { @@ -135,7 +161,7 @@ export default createComponent({ return active; }; - const getTargetOffset = (targetActive, offset = 0) => { + const getTargetOffset = (targetActive: number, offset = 0) => { let currentPosition = targetActive * size.value; if (!props.loop) { currentPosition = Math.min(currentPosition, -minOffset.value); @@ -149,7 +175,15 @@ export default createComponent({ return targetOffset; }; - const move = ({ pace = 0, offset = 0, emitChange }) => { + const move = ({ + pace = 0, + offset = 0, + emitChange, + }: { + pace?: number; + offset?: number; + emitChange?: boolean; + }) => { if (count.value <= 1) { return; } @@ -218,7 +252,7 @@ export default createComponent({ }); }; - let autoplayTimer; + let autoplayTimer: NodeJS.Timeout; const stopAutoplay = () => { clearTimeout(autoplayTimer); @@ -230,7 +264,7 @@ export default createComponent({ autoplayTimer = setTimeout(() => { next(); autoplay(); - }, props.autoplay); + }, +props.autoplay); } }; @@ -249,8 +283,8 @@ export default createComponent({ state.rect = rect; state.swiping = true; state.active = active; - state.width = +props.width || rect.width; - state.height = +props.height || rect.height; + state.width = +(props.width ?? rect.width); + state.height = +(props.height ?? rect.height); state.offset = getTargetOffset(active); children.forEach((swipe) => { swipe.setOffset(0); @@ -263,9 +297,9 @@ export default createComponent({ initialize(state.active); }; - let touchStartTime; + let touchStartTime: number; - const onTouchStart = (event) => { + const onTouchStart = (event: TouchEvent) => { if (!props.touchable) return; touch.start(event); @@ -275,7 +309,7 @@ export default createComponent({ correctPosition(); }; - const onTouchMove = (event) => { + const onTouchMove = (event: TouchEvent) => { if (props.touchable && state.swiping) { touch.move(event); @@ -323,7 +357,7 @@ export default createComponent({ autoplay(); }; - const swipeTo = (index, options = {}) => { + const swipeTo = (index: number, options: SwipeToOptions = {}) => { correctPosition(); touch.reset(); @@ -350,9 +384,11 @@ export default createComponent({ }); }; - const renderDot = (_, index) => { + const renderDot = (_: number, index: number) => { const active = index === activeIndicator.value; - const style = active ? { backgroundColor: props.indicatorColor } : null; + const style: CSSProperties = { + backgroundColor: active ? props.indicatorColor : undefined, + }; return ; }; @@ -379,7 +415,12 @@ export default createComponent({ linkChildren({ size, props, count, activeIndicator }); - watch(() => props.initialSwipe, initialize); + watch( + () => props.initialSwipe, + (value) => { + initialize(+value); + } + ); watch( () => children.length, () => { diff --git a/yarn.lock b/yarn.lock index 2cb774b05..aaaa6371e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1981,10 +1981,10 @@ resolved "https://registry.yarnpkg.com/@vant/touch-emulator/-/touch-emulator-1.2.0.tgz#486300b23e57db9ce9231a04e0a0c621c68692d8" integrity sha512-sJ97zU85zOq51qoi7+CpBEcOyH3CitjP1KC7/GQwqaurUJni+EP7/F9n0HMnAh8GXMjgtgDBNJ5z48x+coNKYQ== -"@vant/use@^1.0.4": - version "1.0.4" - resolved "https://registry.npm.taobao.org/@vant/use/download/@vant/use-1.0.4.tgz#67f827a40e74f3c318d5f05c31751610d8056184" - integrity sha1-Z/gnpA5088MY1fBcMXUWENgFYYQ= +"@vant/use@^1.0.5": + version "1.0.5" + resolved "https://registry.npm.taobao.org/@vant/use/download/@vant/use-1.0.5.tgz#44bee952ebccf9396414a51c4d4b458434a9de23" + integrity sha1-RL7pUuvM+TlkFKUcTUtFhDSp3iM= dependencies: "@babel/runtime" "7.x"