types(Tabs): use tsx (#8187)

This commit is contained in:
neverland 2021-02-20 17:49:34 +08:00 committed by GitHub
parent 9ad0eafae0
commit 906f01cc8e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 106 additions and 61 deletions

View File

@ -33,7 +33,7 @@ export default createComponent({
if (!parent) { if (!parent) {
if (process.env.NODE_ENV !== 'production') { if (process.env.NODE_ENV !== 'production') {
console.error( console.error(
'[Vant] CollapseItem must be a child component of Collapse.' '[Vant] <CollapseItem> must be a child component of <Collapse>.'
); );
} }
return; return;

View File

@ -58,7 +58,7 @@ export default createComponent({
if (!parent) { if (!parent) {
if (process.env.NODE_ENV !== 'production') { if (process.env.NODE_ENV !== 'production') {
console.error( console.error(
'[Vant] DropdownItem must be a child component of DropdownMenu.' '[Vant] <DropdownItem> must be a child component of <DropdownMenu>.'
); );
} }
return; return;

View File

@ -31,7 +31,7 @@ export default createComponent({
if (!parent) { if (!parent) {
if (process.env.NODE_ENV !== 'production') { if (process.env.NODE_ENV !== 'production') {
console.error('[Vant] GridItem must be a child component of Grid.'); console.error('[Vant] <GridItem> must be a child component of <Grid>.');
} }
return; return;
} }

View File

@ -32,7 +32,7 @@ export default createComponent({
if (!parent) { if (!parent) {
if (process.env.NODE_ENV !== 'production') { if (process.env.NODE_ENV !== 'production') {
console.error( console.error(
'[Vant] IndexAnchor must be a child component of IndexBar.' '[Vant] <IndexAnchor> must be a child component of <IndexBar>.'
); );
} }
return; return;

View File

@ -24,7 +24,7 @@ export default createComponent({
if (!parent) { if (!parent) {
if (process.env.NODE_ENV !== 'production') { if (process.env.NODE_ENV !== 'production') {
console.error( console.error(
'[Vant] SidebarItem must be a child component of Sidebar.' '[Vant] <SidebarItem> must be a child component of <Sidebar>.'
); );
} }
return; return;

View File

@ -19,7 +19,7 @@ export default createComponent({
if (!parent) { if (!parent) {
if (process.env.NODE_ENV !== 'production') { if (process.env.NODE_ENV !== 'production') {
console.error('[Vant] Step must be a child component of Steps.'); console.error('[Vant] <Step> must be a child component of <Steps>.');
} }
return; return;
} }

View File

@ -19,7 +19,9 @@ export default createComponent({
if (!parent) { if (!parent) {
if (process.env.NODE_ENV !== 'production') { if (process.env.NODE_ENV !== 'production') {
console.error('[Vant] SwipeItem must be a child component of Swipe.'); console.error(
'[Vant] <SwipeItem> must be a child component of <Swipe>.'
);
} }
return; return;
} }

View File

@ -267,11 +267,11 @@ export default {
| Event | Description | Arguments | | Event | Description | Arguments |
| --- | --- | --- | | --- | --- | --- |
| click | Emitted when a tab is clicked | nametitle | | click | Emitted when a tab is clicked | _name: string \| number, title: string_ |
| change | Emitted when active tab changed | nametitle | | change | Emitted when active tab changed | _name: string \| number, title: string_ |
| disabled | Emitted when a disabled tab is clicked | nametitle | | disabled | Emitted when a disabled tab is clicked | _name: string \| number, title: string_ |
| rendered | Emitted when content first rendered in lazy-render mode | nametitle | | rendered | Emitted when content first rendered in lazy-render mode | _name: string \| number, title: string_ |
| scroll | Emitted when tab scrolling in sticky mode | object: { scrollTop, isFixed } | | scroll | Emitted when tab scrolling in sticky mode | _{ scrollTop: number, isFixed: boolean }_ |
### Tabs Methods ### Tabs Methods
@ -280,7 +280,7 @@ Use [ref](https://v3.vuejs.org/guide/component-template-refs.html) to get Tabs i
| Name | Description | Attribute | Return value | | Name | Description | Attribute | Return value |
| --- | --- | --- | --- | | --- | --- | --- | --- |
| resize | Resize Tabs when container element resized or visibility changed | - | - | | resize | Resize Tabs when container element resized or visibility changed | - | - |
| scrollTo | Go to specified tab in scrollspy mode | name | - | | scrollTo | Go to specified tab in scrollspy mode | _name: string \| number_ | - |
### Tabs Slots ### Tabs Slots

View File

@ -278,11 +278,11 @@ export default {
| 事件名 | 说明 | 回调参数 | | 事件名 | 说明 | 回调参数 |
| --- | --- | --- | | --- | --- | --- |
| click | 点击标签时触发 | name标识符title标题 | | click | 点击标签时触发 | _name: string \| number, title: string_ |
| change | 当前激活的标签改变时触发 | name标识符title标题 | | change | 当前激活的标签改变时触发 | _name: string \| number, title: string_ |
| disabled | 点击被禁用的标签时触发 | name标识符title标题 | | disabled | 点击被禁用的标签时触发 | _name: string \| number, title: string_ |
| rendered | 标签内容首次渲染时触发(仅在开启延迟渲染后触发) | name标识符title标题 | | rendered | 标签内容首次渲染时触发(仅在开启延迟渲染后触发) | _name: string \| number, title: string_ |
| scroll | 滚动时触发,仅在 sticky 模式下生效 | { scrollTop: 距离顶部位置, isFixed: 是否吸顶 } | | scroll | 滚动时触发,仅在 sticky 模式下生效 | _{ scrollTop: number, isFixed: boolean }_ |
### Tabs 方法 ### Tabs 方法
@ -291,7 +291,7 @@ export default {
| 方法名 | 说明 | 参数 | 返回值 | | 方法名 | 说明 | 参数 | 返回值 |
| --- | --- | --- | --- | | --- | --- | --- | --- |
| resize | 外层元素大小或组件显示状态变化时,可以调用此方法来触发重绘 | - | - | | resize | 外层元素大小或组件显示状态变化时,可以调用此方法来触发重绘 | - | - |
| scrollTo | 滚动到指定的标签页,在滚动导航模式下可用 | name: 标识符 | - | | scrollTo | 滚动到指定的标签页,在滚动导航模式下可用 | _name: string \| number_ | - |
### Tabs Slots ### Tabs Slots

View File

@ -1,6 +1,6 @@
import { ref, watch, nextTick } from 'vue'; import { ref, watch, nextTick, PropType, CSSProperties } from 'vue';
import { createNamespace, UnknownProp } from '../utils'; import { createNamespace, UnknownProp } from '../utils';
import { TABS_KEY } from '../tabs'; import { TABS_KEY, TabsProvide } from '../tabs';
// Composition // Composition
import { useParent } from '@vant/use'; import { useParent } from '@vant/use';
@ -20,15 +20,18 @@ export default createComponent({
title: String, title: String,
disabled: Boolean, disabled: Boolean,
titleClass: UnknownProp, titleClass: UnknownProp,
titleStyle: null, titleStyle: [String, Object] as PropType<string | CSSProperties>,
}, },
setup(props, { slots }) { setup(props, { slots }) {
const inited = ref(false); const inited = ref(false);
const { parent, index } = useParent(TABS_KEY); const { parent, index } = useParent<TabsProvide>(TABS_KEY);
if (!parent) { if (!parent) {
throw new Error('[Vant] Tabs: <van-tab> must be used inside <van-tabs>'); if (process.env.NODE_ENV !== 'production') {
console.error('[Vant] <Tab> must be a child component of <Tabs>.');
}
return;
} }
const getName = () => props.name ?? index.value; const getName = () => props.name ?? index.value;
@ -38,7 +41,7 @@ export default createComponent({
if (parent.props.lazyRender) { if (parent.props.lazyRender) {
nextTick(() => { nextTick(() => {
parent.emit('rendered', getName(), props.title); parent.onRendered(getName(), props.title);
}); });
} }
}; };

View File

@ -33,7 +33,9 @@ export default createComponent({
if (!parent) { if (!parent) {
if (process.env.NODE_ENV !== 'production') { if (process.env.NODE_ENV !== 'production') {
console.error('[Vant] TabbarItem must be a child component of Tabbar.'); console.error(
'[Vant] <TabbarItem> must be a child component of <Tabbar>.'
);
} }
return; return;
} }

View File

@ -1,4 +1,15 @@
import { ref, watch, computed, reactive, nextTick, onActivated } from 'vue'; import {
ref,
watch,
computed,
reactive,
nextTick,
PropType,
ComputedRef,
onActivated,
CSSProperties,
ComponentPublicInstance,
} from 'vue';
// Utils // Utils
import { import {
@ -11,10 +22,11 @@ import {
createNamespace, createNamespace,
getVisibleHeight, getVisibleHeight,
setRootScrollTop, setRootScrollTop,
ComponentInstance,
} from '../utils'; } from '../utils';
import { scrollLeftTo, scrollTopTo } from './utils'; import { scrollLeftTo, scrollTopTo } from './utils';
import { BORDER_TOP_BOTTOM } from '../utils/constant'; import { BORDER_TOP_BOTTOM } from '../utils/constant';
import { callInterceptor } from '../utils/interceptor'; import { callInterceptor, Interceptor } from '../utils/interceptor';
// Composition // Composition
import { import {
@ -24,7 +36,7 @@ import {
useEventListener, useEventListener,
onMountedOrActivated, onMountedOrActivated,
} from '@vant/use'; } from '@vant/use';
import { route } from '../composables/use-route'; import { route, RouteProps } from '../composables/use-route';
import { useRefs } from '../composables/use-refs'; import { useRefs } from '../composables/use-refs';
import { useExpose } from '../composables/use-expose'; import { useExpose } from '../composables/use-expose';
@ -37,6 +49,21 @@ const [createComponent, bem] = createNamespace('tabs');
export const TABS_KEY = 'vanTabs'; export const TABS_KEY = 'vanTabs';
export type TabType = 'line' | 'card';
export type TabsProvide = {
props: {
animated?: boolean;
swipeable?: boolean;
scrollspy?: boolean;
lazyRender: boolean;
};
setLine: () => void;
onRendered: (name: string | number, title?: string) => void;
scrollIntoView: (immediate?: boolean) => void;
currentName: ComputedRef<number | string | undefined>;
};
export default createComponent({ export default createComponent({
props: { props: {
color: String, color: String,
@ -48,11 +75,11 @@ export default createComponent({
background: String, background: String,
lineWidth: [Number, String], lineWidth: [Number, String],
lineHeight: [Number, String], lineHeight: [Number, String],
beforeChange: Function, beforeChange: Function as PropType<Interceptor>,
titleActiveColor: String, titleActiveColor: String,
titleInactiveColor: String, titleInactiveColor: String,
type: { type: {
type: String, type: String as PropType<TabType>,
default: 'line', default: 'line',
}, },
active: { active: {
@ -84,18 +111,18 @@ export default createComponent({
emits: ['click', 'change', 'scroll', 'disabled', 'rendered', 'update:active'], emits: ['click', 'change', 'scroll', 'disabled', 'rendered', 'update:active'],
setup(props, { emit, slots }) { setup(props, { emit, slots }) {
let tabHeight; let tabHeight: number;
let lockScroll; let lockScroll: boolean;
let stickyFixed; let stickyFixed: boolean;
const root = ref(); const root = ref<HTMLElement>();
const navRef = ref(); const navRef = ref<HTMLElement>();
const wrapRef = ref(); const wrapRef = ref<HTMLElement>();
const windowSize = useWindowSize(); const windowSize = useWindowSize();
const scroller = useScrollParent(root); const scroller = useScrollParent(root);
const [titleRefs, setTitleRefs] = useRefs(); const [titleRefs, setTitleRefs] = useRefs<ComponentInstance>();
const { children, linkChildren } = useChildren(TABS_KEY); const { children, linkChildren } = useChildren<ComponentInstance>(TABS_KEY);
const state = reactive({ const state = reactive({
inited: false, inited: false,
@ -103,7 +130,7 @@ export default createComponent({
currentIndex: -1, currentIndex: -1,
lineStyle: { lineStyle: {
backgroundColor: props.color, backgroundColor: props.color,
}, } as CSSProperties,
}); });
// whether the nav is scrollable // whether the nav is scrollable
@ -116,7 +143,10 @@ export default createComponent({
background: props.background, background: props.background,
})); }));
const getTabName = (tab, index) => tab.name ?? index; const getTabName = (
tab: ComponentInstance,
index: number
): number | string => tab.name ?? index;
const currentName = computed(() => { const currentName = computed(() => {
const activeTab = children[state.currentIndex]; const activeTab = children[state.currentIndex];
@ -136,11 +166,11 @@ export default createComponent({
}); });
// scroll active tab into view // scroll active tab into view
const scrollIntoView = (immediate) => { const scrollIntoView = (immediate?: boolean) => {
const nav = navRef.value; const nav = navRef.value;
const titles = titleRefs.value; const titles = titleRefs.value;
if (!scrollable.value || !titles || !titles[state.currentIndex]) { if (!scrollable.value || !nav || !titles || !titles[state.currentIndex]) {
return; return;
} }
@ -161,7 +191,7 @@ export default createComponent({
!titles || !titles ||
!titles[state.currentIndex] || !titles[state.currentIndex] ||
props.type !== 'line' || props.type !== 'line' ||
isHidden(root.value) isHidden(root.value!)
) { ) {
return; return;
} }
@ -170,7 +200,7 @@ export default createComponent({
const { lineWidth, lineHeight } = props; const { lineWidth, lineHeight } = props;
const left = title.offsetLeft + title.offsetWidth / 2; const left = title.offsetLeft + title.offsetWidth / 2;
const lineStyle = { const lineStyle: CSSProperties = {
width: addUnit(lineWidth), width: addUnit(lineWidth),
backgroundColor: props.color, backgroundColor: props.color,
transform: `translateX(${left}px) translateX(-50%)`, transform: `translateX(${left}px) translateX(-50%)`,
@ -190,7 +220,7 @@ export default createComponent({
}); });
}; };
const findAvailableTab = (index) => { const findAvailableTab = (index: number) => {
const diff = index < state.currentIndex ? -1 : 1; const diff = index < state.currentIndex ? -1 : 1;
while (index >= 0 && index < children.length) { while (index >= 0 && index < children.length) {
@ -202,7 +232,7 @@ export default createComponent({
} }
}; };
const setCurrentIndex = (currentIndex) => { const setCurrentIndex = (currentIndex: number) => {
const newIndex = findAvailableTab(currentIndex); const newIndex = findAvailableTab(currentIndex);
if (!isDef(newIndex)) { if (!isDef(newIndex)) {
@ -225,7 +255,7 @@ export default createComponent({
}; };
// correct the index of active tab // correct the index of active tab
const setCurrentIndexByName = (name) => { const setCurrentIndexByName = (name: number | string) => {
const matched = children.filter( const matched = children.filter(
(tab, index) => getTabName(tab, index) === name (tab, index) => getTabName(tab, index) === name
); );
@ -238,7 +268,7 @@ export default createComponent({
if (props.scrollspy) { if (props.scrollspy) {
const target = children[state.currentIndex].$el; const target = children[state.currentIndex].$el;
if (target) { if (target && scroller.value) {
const to = getElementTop(target, scroller.value) - scrollOffset.value; const to = getElementTop(target, scroller.value) - scrollOffset.value;
lockScroll = true; lockScroll = true;
@ -255,7 +285,7 @@ export default createComponent({
}; };
// emit event when clicked // emit event when clicked
const onClick = (item, index) => { const onClick = (item: ComponentInstance, index: number) => {
const { title, disabled } = children[index]; const { title, disabled } = children[index];
const name = getTabName(children[index], index); const name = getTabName(children[index], index);
@ -272,16 +302,19 @@ export default createComponent({
}); });
emit('click', name, title); emit('click', name, title);
route(item); route(item as ComponentPublicInstance<RouteProps>);
} }
}; };
const onStickyScroll = (params) => { const onStickyScroll = (params: {
isFixed: boolean;
scrollTop: number;
}) => {
stickyFixed = params.isFixed; stickyFixed = params.isFixed;
emit('scroll', params); emit('scroll', params);
}; };
const scrollTo = (name) => { const scrollTo = (name: number | string) => {
nextTick(() => { nextTick(() => {
setCurrentIndexByName(name); setCurrentIndexByName(name);
scrollToCurrentContent(true); scrollToCurrentContent(true);
@ -390,7 +423,7 @@ export default createComponent({
// scroll to correct position // scroll to correct position
if (stickyFixed && !props.scrollspy) { if (stickyFixed && !props.scrollspy) {
setRootScrollTop( setRootScrollTop(
Math.ceil(getElementTop(root.value) - offsetTopPx.value) Math.ceil(getElementTop(root.value!) - offsetTopPx.value)
); );
} }
} }
@ -400,11 +433,15 @@ export default createComponent({
setCurrentIndexByName(props.active); setCurrentIndexByName(props.active);
nextTick(() => { nextTick(() => {
state.inited = true; state.inited = true;
tabHeight = getVisibleHeight(wrapRef.value); tabHeight = getVisibleHeight(wrapRef.value!);
scrollIntoView(true); scrollIntoView(true);
}); });
}; };
const onRendered = (name: string | number, title?: string) => {
emit('rendered', name, title);
};
useExpose({ useExpose({
resize: setLine, resize: setLine,
scrollTo, scrollTo,
@ -415,9 +452,9 @@ export default createComponent({
useEventListener('scroll', onScroll, { target: scroller.value }); useEventListener('scroll', onScroll, { target: scroller.value });
linkChildren({ linkChildren({
emit,
props, props,
setLine, setLine,
onRendered,
currentName, currentName,
scrollIntoView, scrollIntoView,
}); });

View File

@ -1,5 +1,5 @@
import { raf, cancelRaf } from '@vant/use'; import { raf, cancelRaf } from '@vant/use';
import { getScrollTop, setScrollTop } from '../utils'; import { ScrollElement, getScrollTop, setScrollTop } from '../utils';
let rafId: number; let rafId: number;
@ -26,7 +26,7 @@ export function scrollLeftTo(
} }
export function scrollTopTo( export function scrollTopTo(
scroller: HTMLElement, scroller: ScrollElement,
to: number, to: number,
duration: number, duration: number,
callback: () => void callback: () => void

View File

@ -1,6 +1,6 @@
import { isIOS as checkIsIOS } from '../validate/system'; import { isIOS as checkIsIOS } from '../validate/system';
type ScrollElement = Element | Window; export type ScrollElement = Element | Window;
function isWindow(val: unknown): val is Window { function isWindow(val: unknown): val is Window {
return val === window; return val === window;
@ -36,7 +36,7 @@ export function setRootScrollTop(value: number) {
} }
// get distance from element top to page top or scroller top // get distance from element top to page top or scroller top
export function getElementTop(el: ScrollElement, scroller?: HTMLElement) { export function getElementTop(el: ScrollElement, scroller?: ScrollElement) {
if (isWindow(el)) { if (isWindow(el)) {
return 0; return 0;
} }

View File

@ -20,9 +20,10 @@ declare module 'vue' {
onPress?: EventHandler; onPress?: EventHandler;
onScale?: EventHandler; onScale?: EventHandler;
onCancel?: EventHandler; onCancel?: EventHandler;
onOpened?: EventHandler;
onClosed?: EventHandler; onClosed?: EventHandler;
onChange?: EventHandler; onChange?: EventHandler;
onOpened?: EventHandler;
onScroll?: EventHandler;
onSubmit?: EventHandler; onSubmit?: EventHandler;
onSelect?: EventHandler; onSelect?: EventHandler;
onToggle?: EventHandler; onToggle?: EventHandler;