Skip to content

Commit 8943595

Browse files
committed
feat: tabs overflow node support remove
1 parent 02e6fec commit 8943595

File tree

9 files changed

+100
-48
lines changed

9 files changed

+100
-48
lines changed

components/tabs/src/TabNavList/OperationNode.tsx

Lines changed: 57 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -2,47 +2,37 @@ import Menu, { MenuItem } from '../../../menu';
22
import Dropdown from '../../../vc-dropdown';
33
import type { Tab, TabsLocale, EditableConfig } from '../interface';
44
import AddButton from './AddButton';
5-
import type { Key, VueNode } from '../../../_util/type';
5+
import type { Key } from '../../../_util/type';
66
import KeyCode from '../../../_util/KeyCode';
7-
import type { CSSProperties, PropType } from 'vue';
7+
import type { CSSProperties, ExtractPropTypes, PropType } from 'vue';
88
import classNames from '../../../_util/classNames';
99
import { defineComponent, watch, computed, onMounted } from 'vue';
1010
import PropTypes from '../../../_util/vue-types';
1111
import useState from '../../../_util/hooks/useState';
1212
import { EllipsisOutlined } from '@ant-design/icons-vue';
1313

14-
export interface OperationNodeProps {
15-
prefixCls: string;
16-
id: string;
17-
tabs: Tab[];
18-
rtl: boolean;
19-
tabBarGutter?: number;
20-
activeKey: Key;
21-
mobile: boolean;
22-
moreIcon?: VueNode;
23-
moreTransitionName?: string;
24-
editable?: EditableConfig;
25-
locale?: TabsLocale;
26-
onTabClick: (key: Key, e: MouseEvent | KeyboardEvent) => void;
27-
}
14+
const operationNodeProps = {
15+
prefixCls: { type: String },
16+
id: { type: String },
17+
tabs: { type: Object as PropType<Tab[]> },
18+
rtl: { type: Boolean },
19+
tabBarGutter: { type: Number },
20+
activeKey: { type: [String, Number] },
21+
mobile: { type: Boolean },
22+
moreIcon: PropTypes.any,
23+
moreTransitionName: { type: String },
24+
editable: { type: Object as PropType<EditableConfig> },
25+
locale: { type: Object as PropType<TabsLocale>, default: undefined as TabsLocale },
26+
removeAriaLabel: String,
27+
onTabClick: { type: Function as PropType<(key: Key, e: MouseEvent | KeyboardEvent) => void> },
28+
};
29+
30+
export type OperationNodeProps = Partial<ExtractPropTypes<typeof operationNodeProps>>;
2831

2932
export default defineComponent({
3033
name: 'OperationNode',
3134
inheritAttrs: false,
32-
props: {
33-
prefixCls: { type: String },
34-
id: { type: String },
35-
tabs: { type: Object as PropType<Tab[]> },
36-
rtl: { type: Boolean },
37-
tabBarGutter: { type: Number },
38-
activeKey: { type: [String, Number] },
39-
mobile: { type: Boolean },
40-
moreIcon: PropTypes.any,
41-
moreTransitionName: { type: String },
42-
editable: { type: Object as PropType<EditableConfig> },
43-
locale: { type: Object as PropType<TabsLocale>, default: undefined as TabsLocale },
44-
onTabClick: { type: Function as PropType<(key: Key, e: MouseEvent | KeyboardEvent) => void> },
45-
},
35+
props: operationNodeProps,
4636
emits: ['tabClick'],
4737
slots: ['moreIcon'],
4838
setup(props, { attrs, slots }) {
@@ -99,6 +89,15 @@ export default defineComponent({
9989
selectedKey.value !== null ? `${popupId.value}-${selectedKey.value}` : null,
10090
);
10191

92+
const onRemoveTab = (event: MouseEvent | KeyboardEvent, key: Key) => {
93+
event.preventDefault();
94+
event.stopPropagation();
95+
props.editable.onEdit('remove', {
96+
key,
97+
event,
98+
});
99+
};
100+
102101
onMounted(() => {
103102
watch(
104103
selectedKey,
@@ -174,17 +173,34 @@ export default defineComponent({
174173
dropdownAriaLabel !== undefined ? dropdownAriaLabel : 'expanded dropdown'
175174
}
176175
>
177-
{tabs.map(tab => (
178-
<MenuItem
179-
key={tab.key}
180-
id={`${popupId.value}-${tab.key}`}
181-
role="option"
182-
aria-controls={id && `${id}-panel-${tab.key}`}
183-
disabled={tab.disabled}
184-
>
185-
{typeof tab.tab === 'function' ? tab.tab() : tab.tab}
186-
</MenuItem>
187-
))}
176+
{tabs.map(tab => {
177+
const removable = editable && tab.closable !== false && !tab.disabled;
178+
return (
179+
<MenuItem
180+
key={tab.key}
181+
id={`${popupId.value}-${tab.key}`}
182+
role="option"
183+
aria-controls={id && `${id}-panel-${tab.key}`}
184+
disabled={tab.disabled}
185+
>
186+
<span>{typeof tab.tab === 'function' ? tab.tab() : tab.tab}</span>
187+
{removable && (
188+
<button
189+
type="button"
190+
aria-label={props.removeAriaLabel || 'remove'}
191+
tabindex={0}
192+
class={`${dropdownPrefix}-menu-item-remove`}
193+
onClick={e => {
194+
e.stopPropagation();
195+
onRemoveTab(e, tab.key);
196+
}}
197+
>
198+
{tab.closeIcon?.() || editable.removeIcon?.() || '×'}
199+
</button>
200+
)}
201+
</MenuItem>
202+
);
203+
})}
188204
</Menu>
189205
),
190206
default: () => (

components/tabs/src/TabNavList/index.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -529,6 +529,7 @@ export default defineComponent({
529529
</ResizeObserver>
530530
<OperationNode
531531
{...props}
532+
removeAriaLabel={locale?.removeAriaLabel}
532533
v-slots={pick(slots, ['moreIcon'])}
533534
ref={operationsRef}
534535
prefixCls={pre}

components/tabs/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// base rc-tabs 4.16.6
1+
// base rc-tabs 11.10.1
22
import Tabs from './Tabs';
33
import type { TabsProps } from './Tabs';
44
import TabPane from './TabPanelList/TabPane';

components/tabs/style/card.less

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
@import '../../style/themes/index';
22
@import '../../style/mixins/index';
3-
@import './index';
3+
4+
@tab-prefix-cls: ~'@{ant-prefix}-tabs';
45

56
.@{tab-prefix-cls}-card {
67
> .@{tab-prefix-cls}-nav,

components/tabs/style/dropdown.less

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
@import '../../style/themes/index';
22
@import '../../style/mixins/index';
3-
@import './index';
3+
4+
@tab-prefix-cls: ~'@{ant-prefix}-tabs';
45

56
.@{tab-prefix-cls}-dropdown {
67
.reset-component();
@@ -30,6 +31,8 @@
3031
box-shadow: @box-shadow-base;
3132

3233
&-item {
34+
display: flex;
35+
align-items: center;
3336
min-width: 120px;
3437
margin: 0;
3538
padding: @dropdown-vertical-padding @control-padding-horizontal;
@@ -43,6 +46,25 @@
4346
cursor: pointer;
4447
transition: all 0.3s;
4548

49+
> span {
50+
flex: 1;
51+
white-space: nowrap;
52+
}
53+
54+
&-remove {
55+
flex: none;
56+
margin-left: @margin-sm;
57+
color: @text-color-secondary;
58+
font-size: @font-size-sm;
59+
background: transparent;
60+
border: 0;
61+
cursor: pointer;
62+
63+
&:hover {
64+
color: @tabs-hover-color;
65+
}
66+
}
67+
4668
&:hover {
4769
background: @item-hover-bg;
4870
}

components/tabs/style/index.less

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@
8181

8282
.@{tab-prefix-cls}-nav-add {
8383
min-width: @tabs-card-height;
84+
margin-left: @tabs-card-gutter;
8485
padding: 0 @padding-xs;
8586
background: @tabs-card-head-background;
8687
border: @border-width-base @border-style-base @border-color-split;

components/tabs/style/position.less

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
@import './index';
1+
@import '../../style/themes/index';
2+
@tab-prefix-cls: ~'@{ant-prefix}-tabs';
23

34
.@{tab-prefix-cls} {
45
// ========================== Top & Bottom ==========================
@@ -39,6 +40,7 @@
3940
left: 0;
4041
box-shadow: inset 10px 0 8px -8px fade(@shadow-color, 8%);
4142
}
43+
4244
&::after {
4345
right: 0;
4446
box-shadow: inset -10px 0 8px -8px fade(@shadow-color, 8%);
@@ -122,6 +124,7 @@
122124
top: 0;
123125
box-shadow: inset 0 10px 8px -8px fade(@shadow-color, 8%);
124126
}
127+
125128
&::after {
126129
bottom: 0;
127130
box-shadow: inset 0 -10px 8px -8px fade(@shadow-color, 8%);

components/tabs/style/rtl.less

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,14 @@
5858
> div > .@{tab-prefix-cls}-nav {
5959
.@{tab-prefix-cls}-tab + .@{tab-prefix-cls}-tab {
6060
.@{tab-prefix-cls}-rtl& {
61-
margin-right: 0;
62-
margin-left: @tabs-card-gutter;
61+
margin-right: @tabs-card-gutter;
62+
margin-left: 0;
63+
}
64+
}
65+
.@{tab-prefix-cls}-nav-add {
66+
.@{tab-prefix-cls}-rtl& {
67+
margin-right: @tabs-card-gutter;
68+
margin-left: 0;
6369
}
6470
}
6571
}
@@ -71,6 +77,7 @@
7177
&-rtl {
7278
direction: rtl;
7379
}
80+
7481
&-menu-item {
7582
.@{tab-prefix-cls}-dropdown-rtl & {
7683
text-align: right;

components/tabs/style/size.less

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
@import '../../style/themes/index';
22
@import '../../style/mixins/index';
3-
@import './index';
3+
4+
@tab-prefix-cls: ~'@{ant-prefix}-tabs';
45

56
.@{tab-prefix-cls} {
67
&-small {

0 commit comments

Comments
 (0)