diff --git a/__tests__/renderers/Form/__snapshots__/tabs.test.tsx.snap b/__tests__/renderers/Form/__snapshots__/tabs.test.tsx.snap
index e5b7bea4b..b61744b74 100644
--- a/__tests__/renderers/Form/__snapshots__/tabs.test.tsx.snap
+++ b/__tests__/renderers/Form/__snapshots__/tabs.test.tsx.snap
@@ -37,6 +37,7 @@ exports[`Renderer:tabs 1`] = `
>
.#{$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;
}
}
diff --git a/scss/themes/_cxd-variables.scss b/scss/themes/_cxd-variables.scss
index 8ca42e3a3..abdbcb1f7 100644
--- a/scss/themes/_cxd-variables.scss
+++ b/scss/themes/_cxd-variables.scss
@@ -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)};
diff --git a/src/components/Tabs.tsx b/src/components/Tabs.tsx
index 9e6c2f652..30416bcfe 100644
--- a/src/components/Tabs.tsx
+++ b/src/components/Tabs.tsx
@@ -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
{
static defaultProps: Pick = {
mode: '',
contentClassName: '',
showTip: false,
- showTipClassName: ''
+ showTipClassName: '',
+ sidePosition: 'left',
+ addBtnText: '增加'
};
static Tab = Tab;
- navMain = React.createRef();
+ navMain = React.createRef(); // HTMLDivElement
scroll: boolean = false;
sortable?: Sortable;
dragTip?: HTMLElement;
id: string = guid();
draging: boolean = false;
+ toDispose: Array<() => void> = [];
+ resizeDom = React.createRef();
checkArrowStatus = debounce(
() => {
@@ -200,6 +207,12 @@ export class Tabs extends React.Component {
});
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 {
componentWillUnmount() {
this.checkArrowStatus.cancel();
+ this.toDispose.forEach(fn => fn());
+ this.toDispose = [];
}
/**
@@ -220,7 +235,7 @@ export class Tabs extends React.Component {
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 {
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 {
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 {
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 {
toolbar,
linksClassName,
addable,
- draggable
+ draggable,
+ sidePosition,
+ addBtnText
} = this.props;
const {isOverflow} = this.state;
@@ -617,19 +634,35 @@ export class Tabs extends React.Component {
const mode = tabsMode || dMode;
+ const toolButtons = (
+ <>
+ {addable && (
+ this.handleAddBtn()}>
+
+ {addBtnText}
+
+ )}
+ {toolbar}
+ >
+ );
+
return (
{
- !['vertical', 'chrome'].includes(mode) ? (
-
+ !['vertical', 'sidebar', 'chrome'].includes(mode) ? (
+
{
)}
>
{this.renderArrow('left')}
-
-
+
+
{children.map((tab, index) => this.renderNav(tab, index))}
{additionBtns}
- {toolbar}
+ {
+ !isOverflow && toolButtons
+ }
{this.renderArrow('right')}
{
- addable && (
-
this.handleAddBtn()}>
-
- 增加
-
- )
+ isOverflow && toolButtons
}
) : (
diff --git a/src/renderers/Tabs.tsx b/src/renderers/Tabs.tsx
index b487768cf..4c9e65498 100644
--- a/src/renderers/Tabs.tsx
+++ b/src/renderers/Tabs.tsx
@@ -89,9 +89,9 @@ export interface TabSchema extends Omit
{
*/
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 {
draggable,
showTip,
showTipClassName,
- editable
+ editable,
+ sidePosition,
+ addBtnText
} = this.props;
const mode = tabsMode || dMode;
@@ -747,6 +757,8 @@ export default class Tabs extends React.Component {
showTipClassName={showTipClassName}
editable={editable}
onEdit={this.handleEdit}
+ sidePosition={sidePosition}
+ addBtnText={addBtnText}
>
{children}