refactor: tabs

refactor-tabs
tangjinzhou 2021-10-06 10:18:00 +08:00
parent 53625976b8
commit cd9f592c2e
10 changed files with 137 additions and 126 deletions

View File

@ -42,38 +42,57 @@ import { defineComponent, ref } from 'vue';
export default defineComponent({ export default defineComponent({
setup() { setup() {
return { return {
activeKey: ref(1), activeKey: ref('2'),
}; };
}, },
}); });
</script> </script>
<style> <style>
.card-container { .card-container p {
background: #f5f5f5; margin: 0;
overflow: hidden;
padding: 24px;
} }
.card-container > .ant-tabs-card > .ant-tabs-content { .card-container > .ant-tabs-card .ant-tabs-content {
height: 120px; height: 120px;
margin-top: -16px; margin-top: -16px;
} }
.card-container > .ant-tabs-card .ant-tabs-content > .ant-tabs-tabpane {
.card-container > .ant-tabs-card > .ant-tabs-content > .ant-tabs-tabpane {
background: #fff;
padding: 16px; padding: 16px;
}
.card-container > .ant-tabs-card > .ant-tabs-bar {
border-color: #fff;
}
.card-container > .ant-tabs-card > .ant-tabs-bar .ant-tabs-tab {
border-color: transparent;
background: transparent;
}
.card-container > .ant-tabs-card > .ant-tabs-bar .ant-tabs-tab-active {
border-color: #fff;
background: #fff; background: #fff;
} }
.card-container > .ant-tabs-card > .ant-tabs-nav::before {
display: none;
}
.card-container > .ant-tabs-card .ant-tabs-tab,
[data-theme='compact'] .card-container > .ant-tabs-card .ant-tabs-tab {
background: transparent;
border-color: transparent;
}
.card-container > .ant-tabs-card .ant-tabs-tab-active,
[data-theme='compact'] .card-container > .ant-tabs-card .ant-tabs-tab-active {
background: #fff;
border-color: #fff;
}
#components-tabs-demo-card-top .code-box-demo {
padding: 24px;
overflow: hidden;
background: #f5f5f5;
}
[data-theme='compact'] .card-container > .ant-tabs-card .ant-tabs-content {
height: 120px;
margin-top: -8px;
}
[data-theme='dark'] .card-container > .ant-tabs-card .ant-tabs-tab {
background: transparent;
border-color: transparent;
}
[data-theme='dark'] #components-tabs-demo-card-top .code-box-demo {
background: #000;
}
[data-theme='dark'] .card-container > .ant-tabs-card .ant-tabs-content > .ant-tabs-tabpane {
background: #141414;
}
[data-theme='dark'] .card-container > .ant-tabs-card .ant-tabs-tab-active {
background: #141414;
border-color: #141414;
}
</style> </style>

View File

@ -1,10 +1,10 @@
import type { Tab } from './interface'; import type { Tab } from './interface';
import type { PropType, InjectionKey } from 'vue'; import type { PropType, InjectionKey, Ref } from 'vue';
import { provide, inject, defineComponent } from 'vue'; import { provide, inject, defineComponent, toRefs, ref } from 'vue';
export interface TabContextProps { export interface TabContextProps {
tabs: Tab[]; tabs: Ref<Tab[]>;
prefixCls: string; prefixCls: Ref<string>;
} }
const TabsContextKey: InjectionKey<TabContextProps> = Symbol('tabsContextKey'); const TabsContextKey: InjectionKey<TabContextProps> = Symbol('tabsContextKey');
@ -14,7 +14,7 @@ export const useProvideTabs = (props: TabContextProps) => {
}; };
export const useInjectTabs = () => { export const useInjectTabs = () => {
return inject(TabsContextKey, { tabs: [], prefixCls: undefined }); return inject(TabsContextKey, { tabs: ref([]), prefixCls: ref() });
}; };
const TabsContextProvider = defineComponent({ const TabsContextProvider = defineComponent({
@ -25,7 +25,7 @@ const TabsContextProvider = defineComponent({
prefixCls: { type: String, default: undefined }, prefixCls: { type: String, default: undefined },
}, },
setup(props, { slots }) { setup(props, { slots }) {
useProvideTabs(props); useProvideTabs(toRefs(props));
return () => slots.default?.(); return () => slots.default?.();
}, },
}); });

View File

@ -16,7 +16,7 @@ export interface OperationNodeProps {
tabs: Tab[]; tabs: Tab[];
rtl: boolean; rtl: boolean;
tabBarGutter?: number; tabBarGutter?: number;
activeKey: string; activeKey: Key;
mobile: boolean; mobile: boolean;
moreIcon?: VueNode; moreIcon?: VueNode;
moreTransitionName?: string; moreTransitionName?: string;
@ -34,7 +34,7 @@ export default defineComponent({
tabs: { type: Object as PropType<Tab[]> }, tabs: { type: Object as PropType<Tab[]> },
rtl: { type: Boolean }, rtl: { type: Boolean },
tabBarGutter: { type: Number }, tabBarGutter: { type: Number },
activeKey: { type: String }, activeKey: { type: [String, Number] },
mobile: { type: Boolean }, mobile: { type: Boolean },
moreIcon: PropTypes.any, moreIcon: PropTypes.any,
moreTransitionName: { type: String }, moreTransitionName: { type: String },
@ -47,7 +47,7 @@ export default defineComponent({
setup(props, { attrs, slots }) { setup(props, { attrs, slots }) {
// ======================== Dropdown ======================== // ======================== Dropdown ========================
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const [selectedKey, setSelectedKey] = useState<string>(null); const [selectedKey, setSelectedKey] = useState<Key>(null);
const selectOffset = (offset: -1 | 1) => { const selectOffset = (offset: -1 | 1) => {
const enabledTabs = props.tabs.filter(tab => !tab.disabled); const enabledTabs = props.tabs.filter(tab => !tab.disabled);
let selectedIndex = enabledTabs.findIndex(tab => tab.key === selectedKey.value) || 0; let selectedIndex = enabledTabs.findIndex(tab => tab.key === selectedKey.value) || 0;

View File

@ -32,7 +32,7 @@ const tabNavListProps = () => {
return { return {
id: { type: String }, id: { type: String },
tabPosition: { type: String as PropType<TabPosition> }, tabPosition: { type: String as PropType<TabPosition> },
activeKey: { type: String }, activeKey: { type: [String, Number] },
rtl: { type: Boolean }, rtl: { type: Boolean },
panes: PropTypes.any, panes: PropTypes.any,
animated: { type: Object as PropType<AnimatedConfig>, default: undefined as AnimatedConfig }, animated: { type: Object as PropType<AnimatedConfig>, default: undefined as AnimatedConfig },
@ -66,7 +66,7 @@ export default defineComponent({
slots: ['panes', 'moreIcon', 'extra'], slots: ['panes', 'moreIcon', 'extra'],
emits: ['tabClick', 'tabScroll'], emits: ['tabClick', 'tabScroll'],
setup(props, { attrs, slots }) { setup(props, { attrs, slots }) {
const tabsContext = useInjectTabs(); const { tabs, prefixCls } = useInjectTabs();
const tabsWrapperRef = ref<HTMLDivElement>(); const tabsWrapperRef = ref<HTMLDivElement>();
const tabListRef = ref<HTMLDivElement>(); const tabListRef = ref<HTMLDivElement>();
const operationsRef = ref<{ $el: HTMLDivElement }>(); const operationsRef = ref<{ $el: HTMLDivElement }>();
@ -98,15 +98,10 @@ export default defineComponent({
const [addHeight, setAddHeight] = useState<number>(0); const [addHeight, setAddHeight] = useState<number>(0);
const [tabSizes, setTabSizes] = useRafState<TabSizeMap>(new Map()); const [tabSizes, setTabSizes] = useRafState<TabSizeMap>(new Map());
const tabOffsets = useOffsets( const tabOffsets = useOffsets(tabs, tabSizes);
computed(() => tabsContext.tabs),
tabSizes,
);
// ========================== Util ========================= // ========================== Util =========================
const operationsHiddenClassName = computed( const operationsHiddenClassName = computed(() => `${prefixCls.value}-nav-operations-hidden`);
() => `${tabsContext.prefixCls}-nav-operations-hidden`,
);
const transformMin = ref(0); const transformMin = ref(0);
const transformMax = ref(0); const transformMax = ref(0);
@ -261,15 +256,15 @@ export default defineComponent({
mergedBasicSize = basicSize - addSize; mergedBasicSize = basicSize - addSize;
} }
const { tabs } = tabsContext; const tabsVal = tabs.value;
if (!tabs.length) { if (!tabsVal.length) {
[visibleStart.value, visibleEnd.value] = [0, 0]; [visibleStart.value, visibleEnd.value] = [0, 0];
} }
const len = tabs.length; const len = tabsVal.length;
let endIndex = len; let endIndex = len;
for (let i = 0; i < len; i += 1) { for (let i = 0; i < len; i += 1) {
const offset = tabOffsets.value.get(tabs[i].key) || DEFAULT_SIZE; const offset = tabOffsets.value.get(tabsVal[i].key) || DEFAULT_SIZE;
if (offset[position] + offset[unit] > transformSize + mergedBasicSize) { if (offset[position] + offset[unit] > transformSize + mergedBasicSize) {
endIndex = i - 1; endIndex = i - 1;
break; break;
@ -278,7 +273,7 @@ export default defineComponent({
let startIndex = 0; let startIndex = 0;
for (let i = len - 1; i >= 0; i -= 1) { for (let i = len - 1; i >= 0; i -= 1) {
const offset = tabOffsets.value.get(tabs[i].key) || DEFAULT_SIZE; const offset = tabOffsets.value.get(tabsVal[i].key) || DEFAULT_SIZE;
if (offset[position] < transformSize) { if (offset[position] < transformSize) {
startIndex = i + 1; startIndex = i + 1;
break; break;
@ -319,7 +314,7 @@ export default defineComponent({
// Update buttons records // Update buttons records
setTabSizes(() => { setTabSizes(() => {
const newSizes: TabSizeMap = new Map(); const newSizes: TabSizeMap = new Map();
tabsContext.tabs.forEach(({ key }) => { tabs.value.forEach(({ key }) => {
const btnRef = getBtnRef(key).value; const btnRef = getBtnRef(key).value;
const btnNode = (btnRef as any).$el || btnRef; const btnNode = (btnRef as any).$el || btnRef;
if (btnNode) { if (btnNode) {
@ -337,8 +332,8 @@ export default defineComponent({
// ======================== Dropdown ======================= // ======================== Dropdown =======================
const hiddenTabs = computed(() => [ const hiddenTabs = computed(() => [
...tabsContext.tabs.slice(0, visibleStart.value), ...tabs.value.slice(0, visibleStart.value),
...tabsContext.tabs.slice(visibleEnd.value + 1), ...tabs.value.slice(visibleEnd.value + 1),
]); ]);
// =================== Link & Operations =================== // =================== Link & Operations ===================
@ -385,7 +380,7 @@ export default defineComponent({
); );
watch( watch(
[() => props.rtl, () => props.tabBarGutter, () => props.activeKey, () => tabsContext.tabs], [() => props.rtl, () => props.tabBarGutter, () => props.activeKey, () => tabs.value],
() => { () => {
onListHolderResize(); onListHolderResize();
}, },
@ -406,7 +401,6 @@ export default defineComponent({
}); });
return () => { return () => {
const { prefixCls, tabs } = tabsContext;
const { const {
id, id,
animated, animated,
@ -420,9 +414,10 @@ export default defineComponent({
onTabClick, onTabClick,
} = props; } = props;
const { class: className, style } = attrs; const { class: className, style } = attrs;
const pre = prefixCls.value;
// ========================= Render ======================== // ========================= Render ========================
const hasDropdown = !!hiddenTabs.value.length; const hasDropdown = !!hiddenTabs.value.length;
const wrapPrefix = `${prefixCls}-nav-wrap`; const wrapPrefix = `${pre}-nav-wrap`;
let pingLeft: boolean; let pingLeft: boolean;
let pingRight: boolean; let pingRight: boolean;
let pingTop: boolean; let pingTop: boolean;
@ -450,12 +445,12 @@ export default defineComponent({
typeof tabBarGutter === 'number' ? `${tabBarGutter}px` : tabBarGutter; typeof tabBarGutter === 'number' ? `${tabBarGutter}px` : tabBarGutter;
} }
const tabNodes = tabs.map((tab, i) => { const tabNodes = tabs.value.map((tab, i) => {
const { key } = tab; const { key } = tab;
return ( return (
<TabNode <TabNode
id={id} id={id}
prefixCls={prefixCls} prefixCls={pre}
key={key} key={key}
tab={tab} tab={tab}
/* first node should not have margin left */ /* first node should not have margin left */
@ -492,14 +487,14 @@ export default defineComponent({
<div <div
ref={ref} ref={ref}
role="tablist" role="tablist"
class={classNames(`${prefixCls}-nav`, className)} class={classNames(`${pre}-nav`, className)}
style={style} style={style}
onKeydown={() => { onKeydown={() => {
// No need animation when use keyboard // No need animation when use keyboard
doLockAnimation(); doLockAnimation();
}} }}
> >
<ExtraContent position="left" extra={extra} prefixCls={prefixCls} /> <ExtraContent position="left" extra={extra} prefixCls={pre} />
<ResizeObserver onResize={onListHolderResize}> <ResizeObserver onResize={onListHolderResize}>
<div <div
@ -514,7 +509,7 @@ export default defineComponent({
<ResizeObserver onResize={onListHolderResize}> <ResizeObserver onResize={onListHolderResize}>
<div <div
ref={tabListRef} ref={tabListRef}
class={`${prefixCls}-nav-list`} class={`${pre}-nav-list`}
style={{ style={{
transform: `translate(${transformLeft.value}px, ${transformTop.value}px)`, transform: `translate(${transformLeft.value}px, ${transformTop.value}px)`,
transition: lockAnimation.value ? 'none' : undefined, transition: lockAnimation.value ? 'none' : undefined,
@ -523,7 +518,7 @@ export default defineComponent({
{tabNodes} {tabNodes}
<AddButton <AddButton
ref={innerAddButtonRef} ref={innerAddButtonRef}
prefixCls={prefixCls} prefixCls={pre}
locale={locale} locale={locale}
editable={editable} editable={editable}
style={{ style={{
@ -533,8 +528,8 @@ export default defineComponent({
/> />
<div <div
class={classNames(`${prefixCls}-ink-bar`, { class={classNames(`${pre}-ink-bar`, {
[`${prefixCls}-ink-bar-animated`]: animated.inkBar, [`${pre}-ink-bar-animated`]: animated.inkBar,
})} })}
style={inkStyle.value} style={inkStyle.value}
/> />
@ -546,12 +541,12 @@ export default defineComponent({
<OperationNode <OperationNode
{...props} {...props}
ref={operationsRef} ref={operationsRef}
prefixCls={prefixCls} prefixCls={pre}
tabs={hiddenTabs.value} tabs={hiddenTabs.value}
class={!hasDropdown && operationsHiddenClassName.value} class={!hasDropdown && operationsHiddenClassName.value}
/> />
<ExtraContent position="right" extra={extra} prefixCls={prefixCls} /> <ExtraContent position="right" extra={extra} prefixCls={pre} />
</div> </div>
); );
}; };

View File

@ -1,6 +1,6 @@
import { defineComponent, ref, watch, computed } from 'vue'; import { defineComponent, ref, watch, computed } from 'vue';
import type { CSSProperties } from 'vue'; import type { CSSProperties } from 'vue';
import type { VueNode } from '../../../_util/type'; import type { VueNode, Key } from '../../../_util/type';
import PropTypes from '../../../_util/vue-types'; import PropTypes from '../../../_util/vue-types';
export interface TabPaneProps { export interface TabPaneProps {
@ -12,7 +12,7 @@ export interface TabPaneProps {
// Pass by TabPaneList // Pass by TabPaneList
prefixCls?: string; prefixCls?: string;
tabKey?: string; tabKey?: Key;
id?: string; id?: string;
animated?: boolean; animated?: boolean;
active?: boolean; active?: boolean;
@ -33,7 +33,7 @@ export default defineComponent({
// Pass by TabPaneList // Pass by TabPaneList
prefixCls: { type: String }, prefixCls: { type: String },
tabKey: { type: String }, tabKey: { type: [String, Number] },
id: { type: String }, id: { type: String },
}, },
slots: ['closeIcon', 'tab'], slots: ['closeIcon', 'tab'],

View File

@ -25,22 +25,20 @@ export default defineComponent({
destroyInactiveTabPane: { type: Boolean }, destroyInactiveTabPane: { type: Boolean },
}, },
setup(props) { setup(props) {
const tabsContext = useInjectTabs(); const { tabs, prefixCls } = useInjectTabs();
return () => { return () => {
const { id, activeKey, animated, tabPosition, rtl, destroyInactiveTabPane } = props; const { id, activeKey, animated, tabPosition, rtl, destroyInactiveTabPane } = props;
const { prefixCls, tabs } = tabsContext;
const tabPaneAnimated = animated.tabPane; const tabPaneAnimated = animated.tabPane;
const pre = prefixCls.value;
const activeIndex = tabs.findIndex(tab => tab.key === activeKey); const activeIndex = tabs.value.findIndex(tab => tab.key === activeKey);
return ( return (
<div class={`${prefixCls}-content-holder`}> <div class={`${pre}-content-holder`}>
<div <div
class={[ class={[
`${prefixCls}-content`, `${pre}-content`,
`${prefixCls}-content-${tabPosition}`, `${pre}-content-${tabPosition}`,
{ {
[`${prefixCls}-content-animated`]: tabPaneAnimated, [`${pre}-content-animated`]: tabPaneAnimated,
}, },
]} ]}
style={ style={
@ -49,10 +47,10 @@ export default defineComponent({
: null : null
} }
> >
{tabs.map(tab => { {tabs.value.map(tab => {
return cloneElement(tab.node, { return cloneElement(tab.node, {
key: tab.key, key: tab.key,
prefixCls, prefixCls: pre,
tabKey: tab.key, tabKey: tab.key,
id, id,
animated: tabPaneAnimated, animated: tabPaneAnimated,

View File

@ -21,7 +21,8 @@ import classNames from '../../_util/classNames';
import { CloseOutlined, PlusOutlined } from '@ant-design/icons-vue'; import { CloseOutlined, PlusOutlined } from '@ant-design/icons-vue';
import devWarning from '../../vc-util/devWarning'; import devWarning from '../../vc-util/devWarning';
import type { SizeType } from '../../config-provider'; import type { SizeType } from '../../config-provider';
import TabsContextProvider from './TabContext'; import { useProvideTabs } from './TabContext';
import type { Key } from '../../_util/type';
export type TabsType = 'line' | 'card' | 'editable-card'; export type TabsType = 'line' | 'card' | 'editable-card';
export type TabsPosition = 'top' | 'right' | 'bottom' | 'left'; export type TabsPosition = 'top' | 'right' | 'bottom' | 'left';
@ -34,8 +35,8 @@ export const tabsProps = () => {
prefixCls: { type: String }, prefixCls: { type: String },
id: { type: String }, id: { type: String },
activeKey: { type: String }, activeKey: { type: [String, Number], required: true },
defaultActiveKey: { type: String }, defaultActiveKey: { type: [String, Number] },
direction: { type: String as PropType<'ltr' | 'rtl'> }, direction: { type: String as PropType<'ltr' | 'rtl'> },
animated: { type: [Boolean, Object] as PropType<boolean | AnimatedConfig> }, animated: { type: [Boolean, Object] as PropType<boolean | AnimatedConfig> },
renderTabBar: { type: Function as PropType<RenderTabBar> }, renderTabBar: { type: Function as PropType<RenderTabBar> },
@ -50,12 +51,12 @@ export const tabsProps = () => {
centered: Boolean, centered: Boolean,
onEdit: { onEdit: {
type: Function as PropType< type: Function as PropType<
(e: MouseEvent | KeyboardEvent | string, action: 'add' | 'remove') => void (e: MouseEvent | KeyboardEvent | Key, action: 'add' | 'remove') => void
>, >,
}, },
onChange: { type: Function as PropType<(activeKey: string) => void> }, onChange: { type: Function as PropType<(activeKey: Key) => void> },
onTabClick: { onTabClick: {
type: Function as PropType<(activeKey: string, e: KeyboardEvent | MouseEvent) => void>, type: Function as PropType<(activeKey: Key, e: KeyboardEvent | MouseEvent) => void>,
}, },
onTabScroll: { type: Function as PropType<OnTabScroll> }, onTabScroll: { type: Function as PropType<OnTabScroll> },
@ -78,7 +79,7 @@ function parseTabList(children: any[]): Tab[] {
props[camelize(k)] = v; props[camelize(k)] = v;
} }
const slots = node.children || {}; const slots = node.children || {};
const key = node.key !== undefined ? String(node.key) : undefined; const key = node.key !== undefined ? node.key : undefined;
const { const {
tab = slots.tab, tab = slots.tab,
disabled, disabled,
@ -159,7 +160,7 @@ const InternalTabs = defineComponent({
}); });
// ====================== Active Key ====================== // ====================== Active Key ======================
const [mergedActiveKey, setMergedActiveKey] = useMergedState<string>(() => props.tabs[0]?.key, { const [mergedActiveKey, setMergedActiveKey] = useMergedState<Key>(() => props.tabs[0]?.key, {
value: computed(() => props.activeKey), value: computed(() => props.activeKey),
defaultValue: props.defaultActiveKey, defaultValue: props.defaultActiveKey,
}); });
@ -171,7 +172,7 @@ const InternalTabs = defineComponent({
let newActiveIndex = props.tabs.findIndex(tab => tab.key === mergedActiveKey.value); let newActiveIndex = props.tabs.findIndex(tab => tab.key === mergedActiveKey.value);
if (newActiveIndex === -1) { if (newActiveIndex === -1) {
newActiveIndex = Math.max(0, Math.min(activeIndex.value, props.tabs.length - 1)); newActiveIndex = Math.max(0, Math.min(activeIndex.value, props.tabs.length - 1));
setMergedActiveKey(props.tabs[newActiveIndex]?.key); mergedActiveKey.value = props.tabs[newActiveIndex]?.key;
} }
setActiveIndex(newActiveIndex); setActiveIndex(newActiveIndex);
}); });
@ -197,30 +198,30 @@ const InternalTabs = defineComponent({
}); });
// ======================== Events ======================== // ======================== Events ========================
const onInternalTabClick = (key: string, e: MouseEvent | KeyboardEvent) => { const onInternalTabClick = (key: Key, e: MouseEvent | KeyboardEvent) => {
props.onTabClick?.(key, e); props.onTabClick?.(key, e);
setMergedActiveKey(key); setMergedActiveKey(key);
props.onChange?.(key); props.onChange?.(key);
}; };
useProvideTabs({
tabs: computed(() => props.tabs),
prefixCls,
});
return () => { return () => {
const { const {
id, id,
type, type,
activeKey,
defaultActiveKey,
tabBarGutter, tabBarGutter,
tabBarStyle, tabBarStyle,
locale, locale,
destroyInactiveTabPane, destroyInactiveTabPane,
renderTabBar, renderTabBar,
onChange,
onTabClick,
onTabScroll, onTabScroll,
hideAdd, hideAdd,
centered, centered,
...restProps
} = props; } = props;
// ======================== Render ======================== // ======================== Render ========================
const sharedProps = { const sharedProps = {
@ -274,34 +275,31 @@ const InternalTabs = defineComponent({
const pre = prefixCls.value; const pre = prefixCls.value;
return ( return (
<TabsContextProvider tabs={props.tabs} prefixCls={pre}> <div
<div {...attrs}
{...attrs} id={id}
id={id} class={classNames(
class={classNames( pre,
pre, `${pre}-${mergedTabPosition.value}`,
`${pre}-${mergedTabPosition.value}`, {
{ [`${pre}-${size}`]: size.value,
[`${pre}-${size}`]: size.value, [`${pre}-card`]: ['card', 'editable-card'].includes(type as string),
[`${pre}-card`]: ['card', 'editable-card'].includes(type as string), [`${pre}-editable-card`]: type === 'editable-card',
[`${pre}-editable-card`]: type === 'editable-card', [`${pre}-centered`]: centered,
[`${pre}-centered`]: centered, [`${pre}-mobile`]: mobile.value,
[`${pre}-mobile`]: mobile.value, [`${pre}-editable`]: type === 'editable-card',
[`${pre}-editable`]: type === 'editable-card', [`${pre}-rtl`]: rtl.value,
[`${pre}-rtl`]: rtl.value, },
}, attrs.class,
attrs.class, )}
)} >
{...restProps} {tabNavBar}
> <TabPanelList
{tabNavBar} destroyInactiveTabPane={destroyInactiveTabPane}
<TabPanelList {...sharedProps}
destroyInactiveTabPane={destroyInactiveTabPane} animated={mergedAnimated.value}
{...sharedProps} />
animated={mergedAnimated.value} </div>
/>
</div>
</TabsContextProvider>
); );
}; };
}, },

View File

@ -1,3 +1,4 @@
import supportsPassive from 'ant-design-vue/es/_util/supportsPassive';
import type { Ref } from 'vue'; import type { Ref } from 'vue';
import { ref, onBeforeUnmount, onMounted } from 'vue'; import { ref, onBeforeUnmount, onMounted } from 'vue';
import useState from '../../../_util/hooks/useState'; import useState from '../../../_util/hooks/useState';
@ -9,7 +10,6 @@ const MIN_SWIPE_DISTANCE = 0.1;
const STOP_SWIPE_DISTANCE = 0.01; const STOP_SWIPE_DISTANCE = 0.01;
const REFRESH_INTERVAL = 20; const REFRESH_INTERVAL = 20;
const SPEED_OFF_MULTIPLE = 0.995 ** REFRESH_INTERVAL; const SPEED_OFF_MULTIPLE = 0.995 ** REFRESH_INTERVAL;
// ================================= Hook ================================= // ================================= Hook =================================
export default function useTouchMove( export default function useTouchMove(
domRef: Ref<HTMLDivElement>, domRef: Ref<HTMLDivElement>,
@ -131,7 +131,11 @@ export default function useTouchMove(
// No need to clean up since element removed // No need to clean up since element removed
domRef.value.addEventListener('touchstart', onProxyTouchStart, { passive: false }); domRef.value.addEventListener('touchstart', onProxyTouchStart, { passive: false });
domRef.value.addEventListener('wheel', onProxyWheel); domRef.value.addEventListener(
'wheel',
onProxyWheel,
supportsPassive ? { passive: true } : false,
);
}); });
onBeforeUnmount(() => { onBeforeUnmount(() => {

View File

@ -15,7 +15,7 @@ export type TabOffsetMap = Map<Key, TabOffset>;
export type TabPosition = 'left' | 'right' | 'top' | 'bottom'; export type TabPosition = 'left' | 'right' | 'top' | 'bottom';
export interface Tab extends TabPaneProps { export interface Tab extends TabPaneProps {
key: string; key: Key;
node: VueNode; node: VueNode;
} }
@ -28,10 +28,7 @@ export interface TabsLocale {
} }
export interface EditableConfig { export interface EditableConfig {
onEdit: ( onEdit: (type: 'add' | 'remove', info: { key?: Key; event: MouseEvent | KeyboardEvent }) => void;
type: 'add' | 'remove',
info: { key?: string; event: MouseEvent | KeyboardEvent },
) => void;
showAdd?: boolean; showAdd?: boolean;
removeIcon?: () => VueNode; removeIcon?: () => VueNode;
addIcon?: () => VueNode; addIcon?: () => VueNode;

View File

@ -1,5 +1,5 @@
// debugger tsx // debugger tsx
import Demo from '../../components/tabs/demo/basic.vue'; import Demo from '../../components/tabs/demo/card-top.vue';
export default { export default {
render() { render() {