refactor: tabs
							parent
							
								
									53625976b8
								
							
						
					
					
						commit
						cd9f592c2e
					
				| 
						 | 
				
			
			@ -42,38 +42,57 @@ import { defineComponent, ref } from 'vue';
 | 
			
		|||
export default defineComponent({
 | 
			
		||||
  setup() {
 | 
			
		||||
    return {
 | 
			
		||||
      activeKey: ref(1),
 | 
			
		||||
      activeKey: ref('2'),
 | 
			
		||||
    };
 | 
			
		||||
  },
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
<style>
 | 
			
		||||
.card-container {
 | 
			
		||||
  background: #f5f5f5;
 | 
			
		||||
  overflow: hidden;
 | 
			
		||||
  padding: 24px;
 | 
			
		||||
.card-container p {
 | 
			
		||||
  margin: 0;
 | 
			
		||||
}
 | 
			
		||||
.card-container > .ant-tabs-card > .ant-tabs-content {
 | 
			
		||||
.card-container > .ant-tabs-card .ant-tabs-content {
 | 
			
		||||
  height: 120px;
 | 
			
		||||
  margin-top: -16px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.card-container > .ant-tabs-card > .ant-tabs-content > .ant-tabs-tabpane {
 | 
			
		||||
  background: #fff;
 | 
			
		||||
.card-container > .ant-tabs-card .ant-tabs-content > .ant-tabs-tabpane {
 | 
			
		||||
  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;
 | 
			
		||||
}
 | 
			
		||||
.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>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,10 +1,10 @@
 | 
			
		|||
import type { Tab } from './interface';
 | 
			
		||||
import type { PropType, InjectionKey } from 'vue';
 | 
			
		||||
import { provide, inject, defineComponent } from 'vue';
 | 
			
		||||
import type { PropType, InjectionKey, Ref } from 'vue';
 | 
			
		||||
import { provide, inject, defineComponent, toRefs, ref } from 'vue';
 | 
			
		||||
 | 
			
		||||
export interface TabContextProps {
 | 
			
		||||
  tabs: Tab[];
 | 
			
		||||
  prefixCls: string;
 | 
			
		||||
  tabs: Ref<Tab[]>;
 | 
			
		||||
  prefixCls: Ref<string>;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const TabsContextKey: InjectionKey<TabContextProps> = Symbol('tabsContextKey');
 | 
			
		||||
| 
						 | 
				
			
			@ -14,7 +14,7 @@ export const useProvideTabs = (props: TabContextProps) => {
 | 
			
		|||
};
 | 
			
		||||
 | 
			
		||||
export const useInjectTabs = () => {
 | 
			
		||||
  return inject(TabsContextKey, { tabs: [], prefixCls: undefined });
 | 
			
		||||
  return inject(TabsContextKey, { tabs: ref([]), prefixCls: ref() });
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const TabsContextProvider = defineComponent({
 | 
			
		||||
| 
						 | 
				
			
			@ -25,7 +25,7 @@ const TabsContextProvider = defineComponent({
 | 
			
		|||
    prefixCls: { type: String, default: undefined },
 | 
			
		||||
  },
 | 
			
		||||
  setup(props, { slots }) {
 | 
			
		||||
    useProvideTabs(props);
 | 
			
		||||
    useProvideTabs(toRefs(props));
 | 
			
		||||
    return () => slots.default?.();
 | 
			
		||||
  },
 | 
			
		||||
});
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -16,7 +16,7 @@ export interface OperationNodeProps {
 | 
			
		|||
  tabs: Tab[];
 | 
			
		||||
  rtl: boolean;
 | 
			
		||||
  tabBarGutter?: number;
 | 
			
		||||
  activeKey: string;
 | 
			
		||||
  activeKey: Key;
 | 
			
		||||
  mobile: boolean;
 | 
			
		||||
  moreIcon?: VueNode;
 | 
			
		||||
  moreTransitionName?: string;
 | 
			
		||||
| 
						 | 
				
			
			@ -34,7 +34,7 @@ export default defineComponent({
 | 
			
		|||
    tabs: { type: Object as PropType<Tab[]> },
 | 
			
		||||
    rtl: { type: Boolean },
 | 
			
		||||
    tabBarGutter: { type: Number },
 | 
			
		||||
    activeKey: { type: String },
 | 
			
		||||
    activeKey: { type: [String, Number] },
 | 
			
		||||
    mobile: { type: Boolean },
 | 
			
		||||
    moreIcon: PropTypes.any,
 | 
			
		||||
    moreTransitionName: { type: String },
 | 
			
		||||
| 
						 | 
				
			
			@ -47,7 +47,7 @@ export default defineComponent({
 | 
			
		|||
  setup(props, { attrs, slots }) {
 | 
			
		||||
    // ======================== Dropdown ========================
 | 
			
		||||
    const [open, setOpen] = useState(false);
 | 
			
		||||
    const [selectedKey, setSelectedKey] = useState<string>(null);
 | 
			
		||||
    const [selectedKey, setSelectedKey] = useState<Key>(null);
 | 
			
		||||
    const selectOffset = (offset: -1 | 1) => {
 | 
			
		||||
      const enabledTabs = props.tabs.filter(tab => !tab.disabled);
 | 
			
		||||
      let selectedIndex = enabledTabs.findIndex(tab => tab.key === selectedKey.value) || 0;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -32,7 +32,7 @@ const tabNavListProps = () => {
 | 
			
		|||
  return {
 | 
			
		||||
    id: { type: String },
 | 
			
		||||
    tabPosition: { type: String as PropType<TabPosition> },
 | 
			
		||||
    activeKey: { type: String },
 | 
			
		||||
    activeKey: { type: [String, Number] },
 | 
			
		||||
    rtl: { type: Boolean },
 | 
			
		||||
    panes: PropTypes.any,
 | 
			
		||||
    animated: { type: Object as PropType<AnimatedConfig>, default: undefined as AnimatedConfig },
 | 
			
		||||
| 
						 | 
				
			
			@ -66,7 +66,7 @@ export default defineComponent({
 | 
			
		|||
  slots: ['panes', 'moreIcon', 'extra'],
 | 
			
		||||
  emits: ['tabClick', 'tabScroll'],
 | 
			
		||||
  setup(props, { attrs, slots }) {
 | 
			
		||||
    const tabsContext = useInjectTabs();
 | 
			
		||||
    const { tabs, prefixCls } = useInjectTabs();
 | 
			
		||||
    const tabsWrapperRef = ref<HTMLDivElement>();
 | 
			
		||||
    const tabListRef = ref<HTMLDivElement>();
 | 
			
		||||
    const operationsRef = ref<{ $el: HTMLDivElement }>();
 | 
			
		||||
| 
						 | 
				
			
			@ -98,15 +98,10 @@ export default defineComponent({
 | 
			
		|||
    const [addHeight, setAddHeight] = useState<number>(0);
 | 
			
		||||
 | 
			
		||||
    const [tabSizes, setTabSizes] = useRafState<TabSizeMap>(new Map());
 | 
			
		||||
    const tabOffsets = useOffsets(
 | 
			
		||||
      computed(() => tabsContext.tabs),
 | 
			
		||||
      tabSizes,
 | 
			
		||||
    );
 | 
			
		||||
    const tabOffsets = useOffsets(tabs, tabSizes);
 | 
			
		||||
 | 
			
		||||
    // ========================== Util =========================
 | 
			
		||||
    const operationsHiddenClassName = computed(
 | 
			
		||||
      () => `${tabsContext.prefixCls}-nav-operations-hidden`,
 | 
			
		||||
    );
 | 
			
		||||
    const operationsHiddenClassName = computed(() => `${prefixCls.value}-nav-operations-hidden`);
 | 
			
		||||
 | 
			
		||||
    const transformMin = ref(0);
 | 
			
		||||
    const transformMax = ref(0);
 | 
			
		||||
| 
						 | 
				
			
			@ -261,15 +256,15 @@ export default defineComponent({
 | 
			
		|||
        mergedBasicSize = basicSize - addSize;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      const { tabs } = tabsContext;
 | 
			
		||||
      if (!tabs.length) {
 | 
			
		||||
      const tabsVal = tabs.value;
 | 
			
		||||
      if (!tabsVal.length) {
 | 
			
		||||
        [visibleStart.value, visibleEnd.value] = [0, 0];
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      const len = tabs.length;
 | 
			
		||||
      const len = tabsVal.length;
 | 
			
		||||
      let endIndex = len;
 | 
			
		||||
      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) {
 | 
			
		||||
          endIndex = i - 1;
 | 
			
		||||
          break;
 | 
			
		||||
| 
						 | 
				
			
			@ -278,7 +273,7 @@ export default defineComponent({
 | 
			
		|||
 | 
			
		||||
      let startIndex = 0;
 | 
			
		||||
      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) {
 | 
			
		||||
          startIndex = i + 1;
 | 
			
		||||
          break;
 | 
			
		||||
| 
						 | 
				
			
			@ -319,7 +314,7 @@ export default defineComponent({
 | 
			
		|||
      // Update buttons records
 | 
			
		||||
      setTabSizes(() => {
 | 
			
		||||
        const newSizes: TabSizeMap = new Map();
 | 
			
		||||
        tabsContext.tabs.forEach(({ key }) => {
 | 
			
		||||
        tabs.value.forEach(({ key }) => {
 | 
			
		||||
          const btnRef = getBtnRef(key).value;
 | 
			
		||||
          const btnNode = (btnRef as any).$el || btnRef;
 | 
			
		||||
          if (btnNode) {
 | 
			
		||||
| 
						 | 
				
			
			@ -337,8 +332,8 @@ export default defineComponent({
 | 
			
		|||
 | 
			
		||||
    // ======================== Dropdown =======================
 | 
			
		||||
    const hiddenTabs = computed(() => [
 | 
			
		||||
      ...tabsContext.tabs.slice(0, visibleStart.value),
 | 
			
		||||
      ...tabsContext.tabs.slice(visibleEnd.value + 1),
 | 
			
		||||
      ...tabs.value.slice(0, visibleStart.value),
 | 
			
		||||
      ...tabs.value.slice(visibleEnd.value + 1),
 | 
			
		||||
    ]);
 | 
			
		||||
 | 
			
		||||
    // =================== Link & Operations ===================
 | 
			
		||||
| 
						 | 
				
			
			@ -385,7 +380,7 @@ export default defineComponent({
 | 
			
		|||
    );
 | 
			
		||||
 | 
			
		||||
    watch(
 | 
			
		||||
      [() => props.rtl, () => props.tabBarGutter, () => props.activeKey, () => tabsContext.tabs],
 | 
			
		||||
      [() => props.rtl, () => props.tabBarGutter, () => props.activeKey, () => tabs.value],
 | 
			
		||||
      () => {
 | 
			
		||||
        onListHolderResize();
 | 
			
		||||
      },
 | 
			
		||||
| 
						 | 
				
			
			@ -406,7 +401,6 @@ export default defineComponent({
 | 
			
		|||
    });
 | 
			
		||||
 | 
			
		||||
    return () => {
 | 
			
		||||
      const { prefixCls, tabs } = tabsContext;
 | 
			
		||||
      const {
 | 
			
		||||
        id,
 | 
			
		||||
        animated,
 | 
			
		||||
| 
						 | 
				
			
			@ -420,9 +414,10 @@ export default defineComponent({
 | 
			
		|||
        onTabClick,
 | 
			
		||||
      } = props;
 | 
			
		||||
      const { class: className, style } = attrs;
 | 
			
		||||
      const pre = prefixCls.value;
 | 
			
		||||
      // ========================= Render ========================
 | 
			
		||||
      const hasDropdown = !!hiddenTabs.value.length;
 | 
			
		||||
      const wrapPrefix = `${prefixCls}-nav-wrap`;
 | 
			
		||||
      const wrapPrefix = `${pre}-nav-wrap`;
 | 
			
		||||
      let pingLeft: boolean;
 | 
			
		||||
      let pingRight: boolean;
 | 
			
		||||
      let pingTop: boolean;
 | 
			
		||||
| 
						 | 
				
			
			@ -450,12 +445,12 @@ export default defineComponent({
 | 
			
		|||
          typeof tabBarGutter === 'number' ? `${tabBarGutter}px` : tabBarGutter;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      const tabNodes = tabs.map((tab, i) => {
 | 
			
		||||
      const tabNodes = tabs.value.map((tab, i) => {
 | 
			
		||||
        const { key } = tab;
 | 
			
		||||
        return (
 | 
			
		||||
          <TabNode
 | 
			
		||||
            id={id}
 | 
			
		||||
            prefixCls={prefixCls}
 | 
			
		||||
            prefixCls={pre}
 | 
			
		||||
            key={key}
 | 
			
		||||
            tab={tab}
 | 
			
		||||
            /* first node should not have margin left */
 | 
			
		||||
| 
						 | 
				
			
			@ -492,14 +487,14 @@ export default defineComponent({
 | 
			
		|||
        <div
 | 
			
		||||
          ref={ref}
 | 
			
		||||
          role="tablist"
 | 
			
		||||
          class={classNames(`${prefixCls}-nav`, className)}
 | 
			
		||||
          class={classNames(`${pre}-nav`, className)}
 | 
			
		||||
          style={style}
 | 
			
		||||
          onKeydown={() => {
 | 
			
		||||
            // No need animation when use keyboard
 | 
			
		||||
            doLockAnimation();
 | 
			
		||||
          }}
 | 
			
		||||
        >
 | 
			
		||||
          <ExtraContent position="left" extra={extra} prefixCls={prefixCls} />
 | 
			
		||||
          <ExtraContent position="left" extra={extra} prefixCls={pre} />
 | 
			
		||||
 | 
			
		||||
          <ResizeObserver onResize={onListHolderResize}>
 | 
			
		||||
            <div
 | 
			
		||||
| 
						 | 
				
			
			@ -514,7 +509,7 @@ export default defineComponent({
 | 
			
		|||
              <ResizeObserver onResize={onListHolderResize}>
 | 
			
		||||
                <div
 | 
			
		||||
                  ref={tabListRef}
 | 
			
		||||
                  class={`${prefixCls}-nav-list`}
 | 
			
		||||
                  class={`${pre}-nav-list`}
 | 
			
		||||
                  style={{
 | 
			
		||||
                    transform: `translate(${transformLeft.value}px, ${transformTop.value}px)`,
 | 
			
		||||
                    transition: lockAnimation.value ? 'none' : undefined,
 | 
			
		||||
| 
						 | 
				
			
			@ -523,7 +518,7 @@ export default defineComponent({
 | 
			
		|||
                  {tabNodes}
 | 
			
		||||
                  <AddButton
 | 
			
		||||
                    ref={innerAddButtonRef}
 | 
			
		||||
                    prefixCls={prefixCls}
 | 
			
		||||
                    prefixCls={pre}
 | 
			
		||||
                    locale={locale}
 | 
			
		||||
                    editable={editable}
 | 
			
		||||
                    style={{
 | 
			
		||||
| 
						 | 
				
			
			@ -533,8 +528,8 @@ export default defineComponent({
 | 
			
		|||
                  />
 | 
			
		||||
 | 
			
		||||
                  <div
 | 
			
		||||
                    class={classNames(`${prefixCls}-ink-bar`, {
 | 
			
		||||
                      [`${prefixCls}-ink-bar-animated`]: animated.inkBar,
 | 
			
		||||
                    class={classNames(`${pre}-ink-bar`, {
 | 
			
		||||
                      [`${pre}-ink-bar-animated`]: animated.inkBar,
 | 
			
		||||
                    })}
 | 
			
		||||
                    style={inkStyle.value}
 | 
			
		||||
                  />
 | 
			
		||||
| 
						 | 
				
			
			@ -546,12 +541,12 @@ export default defineComponent({
 | 
			
		|||
          <OperationNode
 | 
			
		||||
            {...props}
 | 
			
		||||
            ref={operationsRef}
 | 
			
		||||
            prefixCls={prefixCls}
 | 
			
		||||
            prefixCls={pre}
 | 
			
		||||
            tabs={hiddenTabs.value}
 | 
			
		||||
            class={!hasDropdown && operationsHiddenClassName.value}
 | 
			
		||||
          />
 | 
			
		||||
 | 
			
		||||
          <ExtraContent position="right" extra={extra} prefixCls={prefixCls} />
 | 
			
		||||
          <ExtraContent position="right" extra={extra} prefixCls={pre} />
 | 
			
		||||
        </div>
 | 
			
		||||
      );
 | 
			
		||||
    };
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,6 +1,6 @@
 | 
			
		|||
import { defineComponent, ref, watch, computed } 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';
 | 
			
		||||
 | 
			
		||||
export interface TabPaneProps {
 | 
			
		||||
| 
						 | 
				
			
			@ -12,7 +12,7 @@ export interface TabPaneProps {
 | 
			
		|||
 | 
			
		||||
  // Pass by TabPaneList
 | 
			
		||||
  prefixCls?: string;
 | 
			
		||||
  tabKey?: string;
 | 
			
		||||
  tabKey?: Key;
 | 
			
		||||
  id?: string;
 | 
			
		||||
  animated?: boolean;
 | 
			
		||||
  active?: boolean;
 | 
			
		||||
| 
						 | 
				
			
			@ -33,7 +33,7 @@ export default defineComponent({
 | 
			
		|||
 | 
			
		||||
    // Pass by TabPaneList
 | 
			
		||||
    prefixCls: { type: String },
 | 
			
		||||
    tabKey: { type: String },
 | 
			
		||||
    tabKey: { type: [String, Number] },
 | 
			
		||||
    id: { type: String },
 | 
			
		||||
  },
 | 
			
		||||
  slots: ['closeIcon', 'tab'],
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -25,22 +25,20 @@ export default defineComponent({
 | 
			
		|||
    destroyInactiveTabPane: { type: Boolean },
 | 
			
		||||
  },
 | 
			
		||||
  setup(props) {
 | 
			
		||||
    const tabsContext = useInjectTabs();
 | 
			
		||||
    const { tabs, prefixCls } = useInjectTabs();
 | 
			
		||||
    return () => {
 | 
			
		||||
      const { id, activeKey, animated, tabPosition, rtl, destroyInactiveTabPane } = props;
 | 
			
		||||
      const { prefixCls, tabs } = tabsContext;
 | 
			
		||||
      const tabPaneAnimated = animated.tabPane;
 | 
			
		||||
 | 
			
		||||
      const activeIndex = tabs.findIndex(tab => tab.key === activeKey);
 | 
			
		||||
 | 
			
		||||
      const pre = prefixCls.value;
 | 
			
		||||
      const activeIndex = tabs.value.findIndex(tab => tab.key === activeKey);
 | 
			
		||||
      return (
 | 
			
		||||
        <div class={`${prefixCls}-content-holder`}>
 | 
			
		||||
        <div class={`${pre}-content-holder`}>
 | 
			
		||||
          <div
 | 
			
		||||
            class={[
 | 
			
		||||
              `${prefixCls}-content`,
 | 
			
		||||
              `${prefixCls}-content-${tabPosition}`,
 | 
			
		||||
              `${pre}-content`,
 | 
			
		||||
              `${pre}-content-${tabPosition}`,
 | 
			
		||||
              {
 | 
			
		||||
                [`${prefixCls}-content-animated`]: tabPaneAnimated,
 | 
			
		||||
                [`${pre}-content-animated`]: tabPaneAnimated,
 | 
			
		||||
              },
 | 
			
		||||
            ]}
 | 
			
		||||
            style={
 | 
			
		||||
| 
						 | 
				
			
			@ -49,10 +47,10 @@ export default defineComponent({
 | 
			
		|||
                : null
 | 
			
		||||
            }
 | 
			
		||||
          >
 | 
			
		||||
            {tabs.map(tab => {
 | 
			
		||||
            {tabs.value.map(tab => {
 | 
			
		||||
              return cloneElement(tab.node, {
 | 
			
		||||
                key: tab.key,
 | 
			
		||||
                prefixCls,
 | 
			
		||||
                prefixCls: pre,
 | 
			
		||||
                tabKey: tab.key,
 | 
			
		||||
                id,
 | 
			
		||||
                animated: tabPaneAnimated,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -21,7 +21,8 @@ import classNames from '../../_util/classNames';
 | 
			
		|||
import { CloseOutlined, PlusOutlined } from '@ant-design/icons-vue';
 | 
			
		||||
import devWarning from '../../vc-util/devWarning';
 | 
			
		||||
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 TabsPosition = 'top' | 'right' | 'bottom' | 'left';
 | 
			
		||||
| 
						 | 
				
			
			@ -34,8 +35,8 @@ export const tabsProps = () => {
 | 
			
		|||
    prefixCls: { type: String },
 | 
			
		||||
    id: { type: String },
 | 
			
		||||
 | 
			
		||||
    activeKey: { type: String },
 | 
			
		||||
    defaultActiveKey: { type: String },
 | 
			
		||||
    activeKey: { type: [String, Number], required: true },
 | 
			
		||||
    defaultActiveKey: { type: [String, Number] },
 | 
			
		||||
    direction: { type: String as PropType<'ltr' | 'rtl'> },
 | 
			
		||||
    animated: { type: [Boolean, Object] as PropType<boolean | AnimatedConfig> },
 | 
			
		||||
    renderTabBar: { type: Function as PropType<RenderTabBar> },
 | 
			
		||||
| 
						 | 
				
			
			@ -50,12 +51,12 @@ export const tabsProps = () => {
 | 
			
		|||
    centered: Boolean,
 | 
			
		||||
    onEdit: {
 | 
			
		||||
      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: {
 | 
			
		||||
      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> },
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -78,7 +79,7 @@ function parseTabList(children: any[]): Tab[] {
 | 
			
		|||
          props[camelize(k)] = v;
 | 
			
		||||
        }
 | 
			
		||||
        const slots = node.children || {};
 | 
			
		||||
        const key = node.key !== undefined ? String(node.key) : undefined;
 | 
			
		||||
        const key = node.key !== undefined ? node.key : undefined;
 | 
			
		||||
        const {
 | 
			
		||||
          tab = slots.tab,
 | 
			
		||||
          disabled,
 | 
			
		||||
| 
						 | 
				
			
			@ -159,7 +160,7 @@ const InternalTabs = defineComponent({
 | 
			
		|||
    });
 | 
			
		||||
 | 
			
		||||
    // ====================== Active Key ======================
 | 
			
		||||
    const [mergedActiveKey, setMergedActiveKey] = useMergedState<string>(() => props.tabs[0]?.key, {
 | 
			
		||||
    const [mergedActiveKey, setMergedActiveKey] = useMergedState<Key>(() => props.tabs[0]?.key, {
 | 
			
		||||
      value: computed(() => props.activeKey),
 | 
			
		||||
      defaultValue: props.defaultActiveKey,
 | 
			
		||||
    });
 | 
			
		||||
| 
						 | 
				
			
			@ -171,7 +172,7 @@ const InternalTabs = defineComponent({
 | 
			
		|||
      let newActiveIndex = props.tabs.findIndex(tab => tab.key === mergedActiveKey.value);
 | 
			
		||||
      if (newActiveIndex === -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);
 | 
			
		||||
    });
 | 
			
		||||
| 
						 | 
				
			
			@ -197,30 +198,30 @@ const InternalTabs = defineComponent({
 | 
			
		|||
    });
 | 
			
		||||
 | 
			
		||||
    // ======================== Events ========================
 | 
			
		||||
    const onInternalTabClick = (key: string, e: MouseEvent | KeyboardEvent) => {
 | 
			
		||||
    const onInternalTabClick = (key: Key, e: MouseEvent | KeyboardEvent) => {
 | 
			
		||||
      props.onTabClick?.(key, e);
 | 
			
		||||
 | 
			
		||||
      setMergedActiveKey(key);
 | 
			
		||||
      props.onChange?.(key);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    useProvideTabs({
 | 
			
		||||
      tabs: computed(() => props.tabs),
 | 
			
		||||
      prefixCls,
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    return () => {
 | 
			
		||||
      const {
 | 
			
		||||
        id,
 | 
			
		||||
        type,
 | 
			
		||||
        activeKey,
 | 
			
		||||
        defaultActiveKey,
 | 
			
		||||
        tabBarGutter,
 | 
			
		||||
        tabBarStyle,
 | 
			
		||||
        locale,
 | 
			
		||||
        destroyInactiveTabPane,
 | 
			
		||||
        renderTabBar,
 | 
			
		||||
        onChange,
 | 
			
		||||
        onTabClick,
 | 
			
		||||
        onTabScroll,
 | 
			
		||||
        hideAdd,
 | 
			
		||||
        centered,
 | 
			
		||||
        ...restProps
 | 
			
		||||
      } = props;
 | 
			
		||||
      // ======================== Render ========================
 | 
			
		||||
      const sharedProps = {
 | 
			
		||||
| 
						 | 
				
			
			@ -274,34 +275,31 @@ const InternalTabs = defineComponent({
 | 
			
		|||
      const pre = prefixCls.value;
 | 
			
		||||
 | 
			
		||||
      return (
 | 
			
		||||
        <TabsContextProvider tabs={props.tabs} prefixCls={pre}>
 | 
			
		||||
          <div
 | 
			
		||||
            {...attrs}
 | 
			
		||||
            id={id}
 | 
			
		||||
            class={classNames(
 | 
			
		||||
              pre,
 | 
			
		||||
              `${pre}-${mergedTabPosition.value}`,
 | 
			
		||||
              {
 | 
			
		||||
                [`${pre}-${size}`]: size.value,
 | 
			
		||||
                [`${pre}-card`]: ['card', 'editable-card'].includes(type as string),
 | 
			
		||||
                [`${pre}-editable-card`]: type === 'editable-card',
 | 
			
		||||
                [`${pre}-centered`]: centered,
 | 
			
		||||
                [`${pre}-mobile`]: mobile.value,
 | 
			
		||||
                [`${pre}-editable`]: type === 'editable-card',
 | 
			
		||||
                [`${pre}-rtl`]: rtl.value,
 | 
			
		||||
              },
 | 
			
		||||
              attrs.class,
 | 
			
		||||
            )}
 | 
			
		||||
            {...restProps}
 | 
			
		||||
          >
 | 
			
		||||
            {tabNavBar}
 | 
			
		||||
            <TabPanelList
 | 
			
		||||
              destroyInactiveTabPane={destroyInactiveTabPane}
 | 
			
		||||
              {...sharedProps}
 | 
			
		||||
              animated={mergedAnimated.value}
 | 
			
		||||
            />
 | 
			
		||||
          </div>
 | 
			
		||||
        </TabsContextProvider>
 | 
			
		||||
        <div
 | 
			
		||||
          {...attrs}
 | 
			
		||||
          id={id}
 | 
			
		||||
          class={classNames(
 | 
			
		||||
            pre,
 | 
			
		||||
            `${pre}-${mergedTabPosition.value}`,
 | 
			
		||||
            {
 | 
			
		||||
              [`${pre}-${size}`]: size.value,
 | 
			
		||||
              [`${pre}-card`]: ['card', 'editable-card'].includes(type as string),
 | 
			
		||||
              [`${pre}-editable-card`]: type === 'editable-card',
 | 
			
		||||
              [`${pre}-centered`]: centered,
 | 
			
		||||
              [`${pre}-mobile`]: mobile.value,
 | 
			
		||||
              [`${pre}-editable`]: type === 'editable-card',
 | 
			
		||||
              [`${pre}-rtl`]: rtl.value,
 | 
			
		||||
            },
 | 
			
		||||
            attrs.class,
 | 
			
		||||
          )}
 | 
			
		||||
        >
 | 
			
		||||
          {tabNavBar}
 | 
			
		||||
          <TabPanelList
 | 
			
		||||
            destroyInactiveTabPane={destroyInactiveTabPane}
 | 
			
		||||
            {...sharedProps}
 | 
			
		||||
            animated={mergedAnimated.value}
 | 
			
		||||
          />
 | 
			
		||||
        </div>
 | 
			
		||||
      );
 | 
			
		||||
    };
 | 
			
		||||
  },
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,3 +1,4 @@
 | 
			
		|||
import supportsPassive from 'ant-design-vue/es/_util/supportsPassive';
 | 
			
		||||
import type { Ref } from 'vue';
 | 
			
		||||
import { ref, onBeforeUnmount, onMounted } from 'vue';
 | 
			
		||||
import useState from '../../../_util/hooks/useState';
 | 
			
		||||
| 
						 | 
				
			
			@ -9,7 +10,6 @@ const MIN_SWIPE_DISTANCE = 0.1;
 | 
			
		|||
const STOP_SWIPE_DISTANCE = 0.01;
 | 
			
		||||
const REFRESH_INTERVAL = 20;
 | 
			
		||||
const SPEED_OFF_MULTIPLE = 0.995 ** REFRESH_INTERVAL;
 | 
			
		||||
 | 
			
		||||
// ================================= Hook =================================
 | 
			
		||||
export default function useTouchMove(
 | 
			
		||||
  domRef: Ref<HTMLDivElement>,
 | 
			
		||||
| 
						 | 
				
			
			@ -131,7 +131,11 @@ export default function useTouchMove(
 | 
			
		|||
 | 
			
		||||
    // No need to clean up since element removed
 | 
			
		||||
    domRef.value.addEventListener('touchstart', onProxyTouchStart, { passive: false });
 | 
			
		||||
    domRef.value.addEventListener('wheel', onProxyWheel);
 | 
			
		||||
    domRef.value.addEventListener(
 | 
			
		||||
      'wheel',
 | 
			
		||||
      onProxyWheel,
 | 
			
		||||
      supportsPassive ? { passive: true } : false,
 | 
			
		||||
    );
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  onBeforeUnmount(() => {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -15,7 +15,7 @@ export type TabOffsetMap = Map<Key, TabOffset>;
 | 
			
		|||
export type TabPosition = 'left' | 'right' | 'top' | 'bottom';
 | 
			
		||||
 | 
			
		||||
export interface Tab extends TabPaneProps {
 | 
			
		||||
  key: string;
 | 
			
		||||
  key: Key;
 | 
			
		||||
  node: VueNode;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -28,10 +28,7 @@ export interface TabsLocale {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
export interface EditableConfig {
 | 
			
		||||
  onEdit: (
 | 
			
		||||
    type: 'add' | 'remove',
 | 
			
		||||
    info: { key?: string; event: MouseEvent | KeyboardEvent },
 | 
			
		||||
  ) => void;
 | 
			
		||||
  onEdit: (type: 'add' | 'remove', info: { key?: Key; event: MouseEvent | KeyboardEvent }) => void;
 | 
			
		||||
  showAdd?: boolean;
 | 
			
		||||
  removeIcon?: () => VueNode;
 | 
			
		||||
  addIcon?: () => VueNode;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,5 +1,5 @@
 | 
			
		|||
// debugger tsx
 | 
			
		||||
import Demo from '../../components/tabs/demo/basic.vue';
 | 
			
		||||
import Demo from '../../components/tabs/demo/card-top.vue';
 | 
			
		||||
 | 
			
		||||
export default {
 | 
			
		||||
  render() {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue