2021-12-20 20:20:47 +08:00

142 lines
3.0 KiB
TypeScript

import {
ref,
watch,
provide,
computed,
nextTick,
defineComponent,
type PropType,
type CSSProperties,
type ExtractPropTypes,
} from 'vue';
// Utils
import {
extend,
truthProp,
unknownProp,
numericProp,
createNamespace,
} from '../utils';
import { TABS_KEY } from '../tabs/Tabs';
// Composables
import { useParent } from '@vant/use';
import { useId } from '../composables/use-id';
import { useExpose } from '../composables/use-expose';
import { routeProps } from '../composables/use-route';
import { TAB_STATUS_KEY } from '../composables/use-tab-status';
// Components
import { SwipeItem } from '../swipe-item';
const [name, bem] = createNamespace('tab');
const tabProps = extend({}, routeProps, {
dot: Boolean,
name: numericProp,
badge: numericProp,
title: String,
disabled: Boolean,
titleClass: unknownProp,
titleStyle: [String, Object] as PropType<string | CSSProperties>,
showZeroBadge: truthProp,
});
export type TabProps = ExtractPropTypes<typeof tabProps>;
export default defineComponent({
name,
props: tabProps,
setup(props, { slots }) {
const id = useId();
const inited = ref(false);
const { parent, index } = useParent(TABS_KEY);
if (!parent) {
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 init = () => {
inited.value = true;
if (parent.props.lazyRender) {
nextTick(() => {
parent.onRendered(getName(), props.title);
});
}
};
const active = computed(() => {
const isActive = getName() === parent.currentName.value;
if (isActive && !inited.value) {
init();
}
return isActive;
});
watch(
() => props.title,
() => {
parent.setLine();
parent.scrollIntoView();
}
);
provide(TAB_STATUS_KEY, active);
return () => {
const label = `${parent.id}-${index.value}`;
const { animated, swipeable, scrollspy, lazyRender } = parent.props;
if (!slots.default && !animated) {
return;
}
const show = scrollspy || active.value;
if (animated || swipeable) {
return (
<SwipeItem
id={id}
role="tabpanel"
class={bem('panel-wrapper', { inactive: !active.value })}
tabindex={active.value ? 0 : -1}
aria-hidden={!active.value}
aria-labelledby={label}
>
<div class={bem('panel')}>{slots.default?.()}</div>
</SwipeItem>
);
}
const shouldRender = inited.value || scrollspy || !lazyRender;
const Content = shouldRender ? slots.default?.() : null;
useExpose({ id });
return (
<div
v-show={show}
id={id}
role="tabpanel"
class={bem('panel')}
tabindex={show ? 0 : -1}
aria-labelledby={label}
>
{Content}
</div>
);
};
},
});