Tabs 宽度问题修改,与样式优化 (#3654)

* fix: tabs 宽度 sensor 问题 和 样式修复

* feat: tabs 新增 editor 样式风格

* feat: tabs 新增 editor 样式风格

* feat: tabs schema fix

* feat: tabs toobar style fix

* feat: tabs md 修改

* feat: tabs md 修改

* feat: tabs snapshots

Co-authored-by: liuzedong02 <liuzedong02@baidu.com>
This commit is contained in:
sansiro 2022-03-01 14:06:22 +08:00 committed by GitHub
parent 7f29c70a5a
commit 608dd5c3fe
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 426 additions and 53 deletions

View File

@ -37,6 +37,7 @@ exports[`Renderer:tabs 1`] = `
>
<div
class="cxd-Tabs-linksContainer-wrapper"
style="position: relative;"
>
<div
class="cxd-Tabs-linksContainer"
@ -65,6 +66,45 @@ exports[`Renderer:tabs 1`] = `
</ul>
</div>
</div>
<div
class="resize-sensor"
style="position: absolute; left: 0px; top: 0px; right: 0px; bottom: 0px; overflow: scroll; z-index: -1; visibility: hidden;"
>
<div
class="resize-sensor-expand"
style="position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: scroll; z-index: -1; visibility: hidden;"
>
<div
style="position: absolute; left: 0px; top: 0px; width: 10px; height: 10px;"
/>
</div>
<div
class="resize-sensor-shrink"
style="position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: scroll; z-index: -1; visibility: hidden;"
>
<div
style="position: absolute; left: 0; top: 0; width: 200%; height: 200%"
/>
</div>
<div
class="resize-sensor-appear"
style="position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: scroll; z-index: -1; visibility: hidden;animation-name: apearSensor; animation-duration: 0.2s;"
/>
</div>
</div>
<div
class="cxd-Tabs-content"

View File

@ -74,7 +74,8 @@ exports[`Renderer:tabs-transfer 1`] = `
class="cxd-Tabs cxd-Tabs--card cxd-TabsTransfer-tabs"
>
<div
class="cxd-Tabs-linksContainer-wrapper"
class="cxd-Tabs-linksContainer-wrapper cxd-Tabs-linksContainer-wrapper--toolbar"
style="position: relative;"
>
<div
class="cxd-Tabs-linksContainer"
@ -123,6 +124,45 @@ exports[`Renderer:tabs-transfer 1`] = `
</ul>
</div>
</div>
<div
class="resize-sensor"
style="position: absolute; left: 0px; top: 0px; right: 0px; bottom: 0px; overflow: scroll; z-index: -1; visibility: hidden;"
>
<div
class="resize-sensor-expand"
style="position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: scroll; z-index: -1; visibility: hidden;"
>
<div
style="position: absolute; left: 0px; top: 0px; width: 10px; height: 10px;"
/>
</div>
<div
class="resize-sensor-shrink"
style="position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: scroll; z-index: -1; visibility: hidden;"
>
<div
style="position: absolute; left: 0; top: 0; width: 200%; height: 200%"
/>
</div>
<div
class="resize-sensor-appear"
style="position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: scroll; z-index: -1; visibility: hidden;animation-name: apearSensor; animation-duration: 0.2s;"
/>
</div>
</div>
<div
class="cxd-Tabs-content"

View File

@ -9,7 +9,8 @@ exports[`Renderer:portlet 1`] = `
class="cxd-Tabs cxd-Tabs--line cxd-Portlet-tab"
>
<div
class="cxd-Tabs-linksContainer-wrapper"
class="cxd-Tabs-linksContainer-wrapper cxd-Tabs-linksContainer-wrapper--toolbar"
style="position: relative;"
>
<div
class="cxd-Tabs-linksContainer"
@ -77,6 +78,45 @@ exports[`Renderer:portlet 1`] = `
</ul>
</div>
</div>
<div
class="resize-sensor"
style="position: absolute; left: 0px; top: 0px; right: 0px; bottom: 0px; overflow: scroll; z-index: -1; visibility: hidden;"
>
<div
class="resize-sensor-expand"
style="position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: scroll; z-index: -1; visibility: hidden;"
>
<div
style="position: absolute; left: 0px; top: 0px; width: 10px; height: 10px;"
/>
</div>
<div
class="resize-sensor-shrink"
style="position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: scroll; z-index: -1; visibility: hidden;"
>
<div
style="position: absolute; left: 0; top: 0; width: 200%; height: 200%"
/>
</div>
<div
class="resize-sensor-appear"
style="position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: scroll; z-index: -1; visibility: hidden;animation-name: apearSensor; animation-duration: 0.2s;"
/>
</div>
</div>
<div
class="cxd-Tabs-content"

View File

@ -7,6 +7,7 @@ exports[`Renderer:tabs 1`] = `
>
<div
class="cxd-Tabs-linksContainer-wrapper"
style="position: relative;"
>
<div
class="cxd-Tabs-linksContainer"
@ -35,6 +36,45 @@ exports[`Renderer:tabs 1`] = `
</ul>
</div>
</div>
<div
class="resize-sensor"
style="position: absolute; left: 0px; top: 0px; right: 0px; bottom: 0px; overflow: scroll; z-index: -1; visibility: hidden;"
>
<div
class="resize-sensor-expand"
style="position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: scroll; z-index: -1; visibility: hidden;"
>
<div
style="position: absolute; left: 0px; top: 0px; width: 10px; height: 10px;"
/>
</div>
<div
class="resize-sensor-shrink"
style="position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: scroll; z-index: -1; visibility: hidden;"
>
<div
style="position: absolute; left: 0; top: 0; width: 200%; height: 200%"
/>
</div>
<div
class="resize-sensor-appear"
style="position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: scroll; z-index: -1; visibility: hidden;animation-name: apearSensor; animation-duration: 0.2s;"
/>
</div>
</div>
<div
class="cxd-Tabs-content"

View File

@ -90,13 +90,14 @@ order: 68
## 可增加、删除
`tab` 设置的 `closable` 优先级高于整体
`tab` 设置的 `closable` 优先级高于整体。使用 `addBtnText` 设置新增按钮文案
```schema: scope="body"
{
"type": "tabs",
"closable": true,
"addable": true,
"addBtnText": "新增Tab",
"tabs": [
{
"title": "Tab 1",
@ -181,7 +182,7 @@ order: 68
```schema: scope="body"
{
"type": "tabs",
"mode": "simple",
"tabsMode": "simple",
"tabs": [
{
"title": "简约(10)",
@ -205,7 +206,7 @@ order: 68
```schema: scope="body"
{
"type": "tabs",
"mode": "strong",
"tabsMode": "strong",
"tabs": [
{
"title": "选项卡1",
@ -228,7 +229,7 @@ order: 68
```schema: scope="body"
{
"type": "tabs",
"mode": "line",
"tabsMode": "line",
"tabs": [
{
"title": "选项卡1",
@ -251,7 +252,7 @@ order: 68
```schema: scope="body"
{
"type": "tabs",
"mode": "card",
"tabsMode": "card",
"tabs": [
{
"title": "选项卡1",
@ -276,7 +277,7 @@ order: 68
```schema: scope="body"
{
"type": "tabs",
"mode": "chrome",
"tabsMode": "chrome",
"tabs": [
{
"title": "选项卡1",
@ -299,7 +300,7 @@ order: 68
```schema: scope="body"
{
"type": "tabs",
"mode": "tiled",
"tabsMode": "tiled",
"tabs": [
{
"title": "选项卡1",
@ -326,7 +327,7 @@ order: 68
```schema: scope="body"
{
"type": "tabs",
"mode": "radio",
"tabsMode": "radio",
"tabs": [
{
"title": "选项卡1",
@ -349,7 +350,7 @@ order: 68
```schema: scope="body"
{
"type": "tabs",
"mode": "vertical",
"tabsMode": "vertical",
"tabs": [
{
"title": "选项卡1",
@ -367,6 +368,30 @@ order: 68
}
```
### 侧边栏模式
使用 `sidePosition` 设置标签栏位置。
```schema: scope="body"
{
"type": "tabs",
"tabsMode": "sidebar",
"sidePosition": "right",
"tabs": [
{
"title": "按钮",
"body": "选项卡内容1",
"icon": "fa fa-square"
},
{
"title": "动作",
"body": "选项卡内容2",
"icon": "fa fa-gavel"
}
]
}
```
## 配置顶部工具栏
@ -379,6 +404,7 @@ order: 68
{
"type": "button",
"label": "按钮",
"size": "sm",
"actionType": "dialog",
"dialog": {
"title": "弹窗标题",
@ -391,7 +417,6 @@ order: 68
"title": "Tab 1",
"tab": "Content 1"
},
{
"title": "Tab 2",
"tab": "Content 2"
@ -533,7 +558,7 @@ order: 68
| --------------------- | --------------------------------- | ----------------------------------- | ------------------------------------------------------------------------------------------ |
| type | `string` | `"tabs"` | 指定为 Tabs 渲染器 |
| className | `string` | | 外层 Dom 的类名 |
| mode | `string` | | 展示模式,取值可以是 `line`、`card`、`radio`、`vertical`、`chrome`、`simple`、`strong` |
| tabsMode | `string` | | 展示模式,取值可以是 `line`、`card`、`radio`、`vertical`、`chrome`、`simple`、`strong`、`tiled`、`sidebar` |
| tabsClassName | `string` | | Tabs Dom 的类名 |
| tabs | `Array` | | tabs 内容 |
| source | `string` | | tabs 关联数据,关联后可以重复生成选项卡 |
@ -541,18 +566,22 @@ order: 68
| toolbarClassName | `string` | | tabs 中工具栏的类名 |
| tabs[x].title | `string` | | Tab 标题 |
| tabs[x].icon | `icon` | | Tab 的图标 |
| tabs[x].iconPosition | `left` / `right` | `left` | Tab 的图标位置 |
| tabs[x].tab | [SchemaNode](../types/schemanode) | | 内容区 |
| tabs[x].hash | `string` | | 设置以后将跟 url 的 hash 对应 |
| tabs[x].reload | `boolean` | | 设置以后内容每次都会重新渲染,对于 crud 的重新拉取很有用 |
| tabs[x].unmountOnExit | `boolean` | | 每次退出都会销毁当前 tab 栏内容 |
| tabs[x].className | `string` | `"bg-white b-l b-r b-b wrapper-md"` | Tab 区域样式 |
| tabs[x].closable | `boolean` | false | 是否支持删除,优先级高于组件的 `closable` |
| tabs[x].disabled | `boolean` | false | 是否禁用 |
| mountOnEnter | `boolean` | false | 只有在点中 tab 的时候才渲染 |
| unmountOnExit | `boolean` | false | 切换 tab 的时候销毁 |
| addable | `boolean` | false | 是否支持新增 |
| addable | `boolean` | false | 是否支持新增 |
| addBtnText | `string` | 增加 | 新增按钮文案 |
| closable | `boolean` | false | 是否支持删除 |
| draggable | `boolean` | false | 是否支持拖拽 |
| showTip | `boolean` | false | 是否支持提示 |
| showTipClassName | `string` | `'' ` | 提示的类 |
| editable | `boolean` | false | 收否可编辑标签名 |
| scrollable | `boolean` | true | 是否导航支持内容溢出滚动。(属性废弃) |
| sidePosition | `left` / `right` | `left` | `sidebar` 模式下,标签栏位置

View File

@ -1314,6 +1314,7 @@
--Tabs--line-content-padding: var(--gap-base) 0;
--Tabs--line-linkMargin: 0 32px 0 0;
--Tabs--line-linkPadding: 0 0 8px;
--Tabs--line-addPadding: 0 0 10px;
--Tabs--line-padding: var(--gap-md);
--Tabs--line-onHover-borderColor: var(--primary);
--Tabs--line-onHover-color: var(--primary);
@ -1337,14 +1338,22 @@
--Tabs-onActive-color: #{$gray700};
--Tabs-onDisabled-color: #{$gray600};
--Tabs-onHover-borderColor: #{$gray200};
--Tabs-add-icon-size: #{px2rem(16px)};
--Tabs-add-icon-size: #{px2rem(15px)};
--Tabs-add-icon-padding: #{px2rem(1px)};
--Tabs-add-icon-margin: var(--gap-xs);
--Tabs-add-margin: var(--gap-lg);
--Tabs-gray-color: #83868c;
--Tabs-close-margin: #{var(--gap-xs)};
--Tabs-close-marginTop: #{px2rem(1px)};
--Tabs-close-size: #{px2rem(12px)};
--Tabs-link-maxWidth: #{px2rem(160px)};
--Tabs--sidebar-sideWidth: #{px2rem(62px)};
--Tabs--sidebar-sidePadding: #{px2rem(27px)};
--Tabs--sidebar-sideMargin: 0 0 #{px2rem(22px)} 0;
--Tabs--sidebar-iconSize: #{px2rem(24px)};
--Tabs--sidebar-iconMargin: #{px2rem(5px)};
--Tabs--vertical-bg: var(--Table-thead-bg);
--Tabs--vertical-width: #{px2rem(140px)};
--Tabs--vertical-onActive-color: var(--primary);

View File

@ -21,7 +21,7 @@
margin-left: var(--Tabs-add-margin);
align-items: center;
justify-content: flex-start;
padding: var(--Tabs--line-linkPadding);
padding: var(--Tabs--line-addPadding);
white-space: nowrap;
cursor: pointer;
@ -36,9 +36,16 @@
margin-left: 4px;
line-height: 1;
}
&:hover {
color: var(--primary);
fill: var(--primary);
}
}
.#{$ns}Tabs-linksContainer {
flex-grow: 1;
position: relative;
display: flex;
align-items: center;
@ -53,7 +60,7 @@
// }
&-arrow {
margin-bottom: 7px;
margin: var(--Tabs--line-addPadding);
width: 16px;
height: 100%;
display: flex;
@ -89,6 +96,8 @@
}
&-main {
position: relative;
overflow-y: auto;
scrollbar-width: none;
width: 100%;
@ -97,8 +106,11 @@
}
.#{$ns}Tabs-links {
position: relative;
// position: relative;
min-width: 100%;
max-width: 0;
height: 100%;
overflow-x: hidden;
.#{$ns}Tabs-links-drag {
position: absolute;
@ -133,7 +145,9 @@
}
.#{$ns}Tabs-link-close {
margin-left: var( --Tabs-close-margin);
margin: var(--Tabs-close-marginTop) 0 0 var(--Tabs-close-margin);
fill: var(--Tabs-gray-color);
cursor: pointer;
.#{$ns}Tabs-link-close-icon {
width: var(--Tabs-close-size);
@ -223,6 +237,20 @@
&--line {
> .#{$ns}Tabs-linksContainer-wrapper {
border-bottom: var(--Tabs-borderWidth) solid var(--Tabs--simple-split-color);
&--toolbar {
.#{$ns}Tabs-link {
padding-top: 10px;
}
.#{$ns}Tabs-addable {
padding: 0;
}
.#{$ns}Tabs-linksContainer-arrow {
margin: 0;
}
}
}
> .#{$ns}Tabs-linksContainer > .#{$ns}Tabs-linksContainer-arrow {
@ -248,15 +276,22 @@
> a:first-child {
border-width: 0 0 var(--Tabs--line-borderWidth) 0;
padding: var(--Tabs--line-linkPadding);
}
&:hover,
&:focus {
&:hover {
a:first-child {
color: var(--primary);
background: transparent;
border-color: transparent;
}
.#{$ns}Tabs-link-close {
fill: var(--primary);
}
}
&:last-child {
> a {
margin: 0;
@ -298,6 +333,10 @@
}
> .#{$ns}Tabs-linksContainer {
.#{$ns}Tabs-addable {
padding: 0;
}
> .#{$ns}Tabs-linksContainer-arrow {
padding: var(--Tabs--card-arrow-gap) var(--Tabs--card-add-gap) 0;
margin-bottom: 0;
@ -335,6 +374,11 @@
}
}
}
> .#{$ns}Tabs-toolbar {
padding: var(--Tabs--card-padding);
margin: 0;
}
}
> .#{$ns}Tabs-content {
@ -346,7 +390,7 @@
> .#{$ns}Tabs-linksContainer-wrapper {
margin-bottom: px2rem(10px);
> .#{$ns}Tabs-addable {
.#{$ns}Tabs-addable {
padding: 0;
}
@ -446,7 +490,7 @@
}
}
> .#{$ns}Tabs-addable {
.#{$ns}Tabs-addable {
padding: 0 var(--Tabs--tiled-add-gap);
margin-left: 0;
white-space: nowrap;
@ -558,6 +602,82 @@
}
}
&--sidebar {
display: flex;
height: 100%;
&.sidebar--left {
flex-direction: row;
> .#{$ns}Tabs-content {
border-right: none;
}
}
&.sidebar--right {
flex-direction: row-reverse;
> .#{$ns}Tabs-content {
border-left: none;
}
}
> .#{$ns}Tabs-linksWrapper {
flex: 0 0 var(--Tabs--sidebar-sideWidth);
align-items: flex-start;
border: none;
}
> .#{$ns}Tabs-linksWrapper > .#{$ns}Tabs-links {
position: relative;
margin: 0;
padding-top: var(--Tabs--sidebar-sidePadding);
flex-grow: 1;
border: none;
flex-direction: column;
border: 0;
> li {
display: flex;
margin: var(--Tabs--sidebar-sideMargin);
padding: 0;
flex-direction: column;
justify-content: flex-start;
align-items: center;
border: 0;
> a:first-child {
padding: 0;
border: 0;
margin: 0;
display: flex;
flex-direction: column;
align-items: center;
color: var(--Tabs-gray-color);
.#{$ns}Icon {
font-size: var(--Tabs--sidebar-iconSize);
height: var(--Tabs--sidebar-iconSize);
margin-bottom: var(--Tabs--sidebar-iconMargin);
}
}
&.is-active > a:first-child,
> a:first-child:hover,
> a:first-child:focus {
color: var(--Tabs--sidebar-iconColor)
}
}
}
> .#{$ns}Tabs-content {
flex-grow: 1;
border-bottom: none;
> .#{$ns}Tabs-pane {
height: 100%;
}
}
}
&--chrome {
> .#{$ns}Tabs-linksWrapper {
background: var(--Tabs--chrome-bg);
@ -672,7 +792,7 @@
& > .#{$ns}Tabs-linksContainer-wrapper {
border-bottom: none;
& > .#{$ns}Tabs-addable {
.#{$ns}Tabs-addable {
padding: 0;
}
@ -718,6 +838,10 @@
border: none;
}
}
&:hover .#{$ns}Tabs-link-close {
fill: var(--primary);
}
& > a:first-child {
padding: 0;
@ -743,7 +867,7 @@
align-items: stretch;
border-bottom: var(--Tabs-borderWidth) solid var(--Tabs--simple-split-color);
& > .#{$ns}Tabs-addable {
.#{$ns}Tabs-addable {
width: var(--Tabs--strong-add-size);
margin-left: var(--Tabs--card-arrow-gap);
padding: 0;
@ -824,6 +948,10 @@
}
}
&:hover .#{$ns}Tabs-link-close {
fill: var(--primary);
}
&:last-of-type {
margin-right: 0;
}
@ -839,8 +967,12 @@
}
&-toolbar {
display: inline-block;
float: right;
padding-top: var(--gap-xs);
// display: inline-block;
// float: right;
// padding-top: var(--gap-xs);
margin-left: var(--gap-base);
display: flex;
flex-direction: row;
align-items: center;
}
}

View File

@ -504,6 +504,7 @@ $L1: 0px 4px 6px 0px rgba(8, 14, 26, 0.06),
// Tabs
--Tabs-linkFontSize: #{px2rem(14px)};
--Tabs-link-disabled-color: #{$G6};
--Tabs--card-onActive-borderColor: var(--white);
--Tabs--card-bg: var(--Table-thead-bg);
--Tabs--radio-bg: var(--white);
@ -511,7 +512,7 @@ $L1: 0px 4px 6px 0px rgba(8, 14, 26, 0.06),
--Tabs--chrome-bg: #f5f5f5;
--Tabs--simple-split-color: #{$G9};
--Tabs--simple-split-size: var(--gap-xs) var(--gap-md);
--Tabs-link-disabled-color: #{$G6};
--Tabs--sidebar-iconColor: #{$B6};
// Pagination
--Pagination-fontSize: #{px2rem(12px)};

View File

@ -16,6 +16,7 @@ import {Icon} from './icons';
import debounce from 'lodash/debounce';
import {findDOMNode} from 'react-dom';
import TooltipWrapper, {TooltipObject, Trigger} from './TooltipWrapper';
import {resizeSensor} from '../utils/resize-sensor';
import Sortable from 'sortablejs';
@ -26,7 +27,7 @@ const transitionStyles: {
[ENTERED]: 'in'
};
export type TabsMode = '' | 'line' | 'card' | 'radio' | 'vertical' | 'chrome' | 'simple' | 'strong';
export type TabsMode = '' | 'line' | 'card' | 'radio' | 'vertical' | 'chrome' | 'simple' | 'strong' | 'tiled' |'sidebar';
export interface TabProps extends ThemeProps {
title?: string | React.ReactNode; // 标题
@ -114,6 +115,8 @@ export interface TabsProps extends ThemeProps {
scrollable?: boolean; // 属性废弃,为了兼容暂且保留
editable?: boolean;
onEdit?: (index: number, text: string) => void;
sidePosition?: 'left' | 'right';
addBtnText?: string;
}
export interface IDragInfo {
@ -122,21 +125,25 @@ export interface IDragInfo {
export class Tabs extends React.Component<TabsProps, any> {
static defaultProps: Pick<TabsProps,
'mode' | 'contentClassName' | 'showTip' | 'showTipClassName'
'mode' | 'contentClassName' | 'showTip' | 'showTipClassName' | 'sidePosition' | 'addBtnText'
> = {
mode: '',
contentClassName: '',
showTip: false,
showTipClassName: ''
showTipClassName: '',
sidePosition: 'left',
addBtnText: '增加'
};
static Tab = Tab;
navMain = React.createRef<HTMLDivElement>();
navMain = React.createRef<HTMLUListElement>(); // HTMLDivElement
scroll: boolean = false;
sortable?: Sortable;
dragTip?: HTMLElement;
id: string = guid();
draging: boolean = false;
toDispose: Array<() => void> = [];
resizeDom = React.createRef<HTMLDivElement>();
checkArrowStatus = debounce(
() => {
@ -200,6 +207,12 @@ export class Tabs extends React.Component<TabsProps, any> {
});
this.checkArrowStatus();
}
this.resizeDom?.current && this.toDispose.push(
resizeSensor(this.resizeDom.current as HTMLElement, () =>
this.computedWidth()
)
);
}
componentDidUpdate() {
@ -212,6 +225,8 @@ export class Tabs extends React.Component<TabsProps, any> {
componentWillUnmount() {
this.checkArrowStatus.cancel();
this.toDispose.forEach(fn => fn());
this.toDispose = [];
}
/**
@ -220,7 +235,7 @@ export class Tabs extends React.Component<TabsProps, any> {
computedWidth() {
const {mode: dMode, tabsMode} = this.props;
const mode = tabsMode || dMode;
if (mode === 'vertical') {
if (['vertical', 'sidebar'].includes(mode)) {
return;
}
@ -246,7 +261,7 @@ export class Tabs extends React.Component<TabsProps, any> {
const {mode: dMode, tabsMode} = this.props;
const {isOverflow} = this.state;
const mode = tabsMode || dMode;
if (mode === 'vertical' || !isOverflow) {
if (['vertical', 'sidebar'].includes(mode) || !isOverflow) {
return;
}
const {activeKey, children} = this.props;
@ -254,7 +269,7 @@ export class Tabs extends React.Component<TabsProps, any> {
const currentIndex = (children as any[])?.findIndex(
(item: any) => item.props.eventKey === currentKey
);
const li = this.navMain.current?.children[0]?.children || [];
const li = this.navMain.current?.children || [];
const currentLi = li[currentIndex] as HTMLElement;
const liOffsetLeft = currentLi?.offsetLeft;
const liClientWidth = currentLi?.clientWidth;
@ -569,7 +584,7 @@ export class Tabs extends React.Component<TabsProps, any> {
renderArrow(type: 'left' | 'right') {
const {mode: dMode, tabsMode} = this.props;
const mode = tabsMode || dMode;
if (mode === 'vertical') {
if (['vertical', 'sidebar'].includes(mode)) {
return;
}
const {classnames: cx} = this.props;
@ -607,7 +622,9 @@ export class Tabs extends React.Component<TabsProps, any> {
toolbar,
linksClassName,
addable,
draggable
draggable,
sidePosition,
addBtnText
} = this.props;
const {isOverflow} = this.state;
@ -617,19 +634,35 @@ export class Tabs extends React.Component<TabsProps, any> {
const mode = tabsMode || dMode;
const toolButtons = (
<>
{addable && (
<div className={cx('Tabs-addable')} onClick={() => this.handleAddBtn()}>
<Icon icon="plus" className={cx('Tabs-addable-icon')} />
{addBtnText}
</div>
)}
{toolbar}
</>
);
return (
<div
className={cx(
`Tabs`,
{
[`Tabs--${mode}`]: mode
[`Tabs--${mode}`]: mode,
[`sidebar--${sidePosition}`]: mode === 'sidebar'
},
className
)}
>
{
!['vertical', 'chrome'].includes(mode) ? (
<div className={cx('Tabs-linksContainer-wrapper')}>
!['vertical', 'sidebar', 'chrome'].includes(mode) ? (
<div
className={cx('Tabs-linksContainer-wrapper', toolbar && 'Tabs-linksContainer-wrapper--toolbar')}
ref={this.resizeDom}
>
<div
className={cx(
'Tabs-linksContainer',
@ -637,22 +670,19 @@ export class Tabs extends React.Component<TabsProps, any> {
)}
>
{this.renderArrow('left')}
<div className={cx('Tabs-linksContainer-main')} ref={this.navMain}>
<ul className={cx('Tabs-links', linksClassName)} role="tablist">
<div className={cx('Tabs-linksContainer-main')}>
<ul className={cx('Tabs-links', linksClassName)} role="tablist" ref={this.navMain}>
{children.map((tab, index) => this.renderNav(tab, index))}
{additionBtns}
{toolbar}
{
!isOverflow && toolButtons
}
</ul>
</div>
{this.renderArrow('right')}
</div>
{
addable && (
<div className={cx('Tabs-addable')} onClick={() => this.handleAddBtn()}>
<Icon icon="plus" className={cx('Tabs-addable-icon')} />
</div>
)
isOverflow && toolButtons
}
</div>
) : (

View File

@ -89,9 +89,9 @@ export interface TabSchema extends Omit<BaseSchema, 'type'> {
*/
horizontal?: FormSchemaHorizontal;
/**
* tabs closeable
* tabs closable
*/
closeable?: boolean;
closable?: boolean;
/**
*
*/
@ -186,6 +186,14 @@ export interface TabsSchema extends BaseSchema {
*
*/
scrollable?: boolean;
/**
*
*/
sidePosition?: 'left' | 'right';
/**
*
*/
addBtnText?: string;
}
export interface TabsProps
@ -643,7 +651,9 @@ export default class Tabs extends React.Component<TabsProps, TabsState> {
draggable,
showTip,
showTipClassName,
editable
editable,
sidePosition,
addBtnText
} = this.props;
const mode = tabsMode || dMode;
@ -747,6 +757,8 @@ export default class Tabs extends React.Component<TabsProps, TabsState> {
showTipClassName={showTipClassName}
editable={editable}
onEdit={this.handleEdit}
sidePosition={sidePosition}
addBtnText={addBtnText}
>
{children}
</CTabs>