Skip to content

Commit

Permalink
Merge pull request #2416 from DouyinFE/feat/tabs-liveStream
Browse files Browse the repository at this point in the history
feat: Tabs add slash type, renderArrow add defaultNode params, closab…
  • Loading branch information
DaiQiangReal committed Sep 2, 2024
2 parents be6e011 + da56c0e commit b797aa1
Show file tree
Hide file tree
Showing 11 changed files with 267 additions and 32 deletions.
49 changes: 45 additions & 4 deletions content/navigation/tabs/index-en-US.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import { Tabs, TabPane } from '@douyinfe/semi-ui';

### Basic Usage

Tbs supports three types of styles: `line`, `button`, and `card`. By default, the first tab is selected.
Tbs supports three types of styles: `line`, `button`, `card`, and `slash`. By default, the first tab is selected.

Tabs supports two declare ways, and the rendering process of the two is different:

Expand Down Expand Up @@ -168,6 +168,44 @@ class TabDemo extends React.Component {
}
```
```jsx live=true
import React from 'react';
import { Tabs } from '@douyinfe/semi-ui';

class TabDemo extends React.Component {
constructor() {
super();
this.state = { key: '1' };
this.onTabClick = this.onTabClick.bind(this);
}

onTabClick(key, type) {
this.setState({ [type]: key });
}

render() {
// eslint-disable-next-line react/jsx-key
const contentList = [<div>Document</div>, <div>Quick Start</div>, <div>Help</div>];
const tabList = [
{ tab: 'Document', itemKey: '1' },
{ tab: 'Quick Start', itemKey: '2' },
{ tab: 'Help', itemKey: '3' },
];
return (
<Tabs
type="slash"
tabList={tabList}
onChange={key => {
this.onTabClick(key, 'key');
}}
>
{contentList[this.state.key - 1]}
</Tabs>
);
}
}
```
### With Icon
```jsx live=true
Expand Down Expand Up @@ -274,7 +312,7 @@ function Demo() {
### Vertical mode
Support two positions: `tabPosition='left|top'`
When `type` is `line`, `card`, or `button`, horizontal and vertical modes are supported, `tabPosition='left|top'`,default is top. When `type` is `slash`, only horizontal mode is supported.
```jsx live=true
import React from 'react';
Expand Down Expand Up @@ -381,7 +419,9 @@ class App extends React.Component {
**Modify the scrolling rendering Arrow**
`renderArrow` modifies the Arrow, with the input parameters being the overflowed items and position
`renderArrow` modifies the Arrow, with the input parameters being the overflowed items, position, click function, and defaultNode.
**Attention**: The first three parameters of renderArrow are supported since 2.61.0,while defaultNode parameter is supported since 2.66.0.
```jsx live=true dir="column"
import React from 'react';
Expand Down Expand Up @@ -701,14 +741,15 @@ class App extends React.Component {
| activeKey | The itemKey value of the currently active tab page | string | None |
| className | class name | string | None |
| collapsible | collapsed Tabs, **>=1.1.0** | boolean | false |
| dropdownProps | In collapsible mode, It is used to transparently transmit parameters to the Dropdown component of the drop-down menu, support since 2.66.0 | DropDownProps | { start: DropdownProps, end: DropdownProps } |
| visibleTabsStyle | Overall scrolling area style **>=2.61.0** | style: CSSProperties | None |
| contentStyle | The outer style object of the content area | CSSProperties | None |
| defaultActiveKey | Initialize the key value of the selected tab page | string | '1' |
| keepDOM | Whether to render the DOM structure of the hidden panel when using TabPane writing, **>=1.0.0** | boolean | true |
| lazyRender | Lazy rendering, only when the panel is activated will it be rendered in the DOM tree, **>=1.0.0** | boolean | false |
| more | Render a portion of the Tab into a drop-down menu ** >= 2.59.0** | number \| {count:number,render:()=>ReactNode,dropdownProps:DropDownProps} | - |
| renderTabBar | Used for secondary packaging tab bar | (tabBarProps: object, defaultTabBar: React.ComponentType) => ReactNode | None |
| renderArrow | Customize how overflow items indicator are rendered externally. By default, the overflow items are expanded when the arrow button is hovered. **>=2.61.0** | (items: OverflowItem[],pos:"start"\|"end", handleArrowClick:()=>void)=> ReactNode | None |
| renderArrow | Customize how overflow items indicator are rendered externally. By default, the overflow items are expanded when the arrow button is hovered. The first three parameters of renderArrow are supported since **>=2.61.0**, defaultNode is supported since **>=2.66.0** | (items: OverflowItem[],pos:"start"\|"end", handleArrowClick:()=>void, defaultNode: ReactNode)=> ReactNode | None |
| preventScroll | Indicates whether the browser should scroll the document to display the newly focused element, acting on the focus method inside the component, excluding the component passed in by the user | boolean |
| showRestInDropdown | Whether to display the collapsed Tab in the drop-down menu (only effective when collapsible is true) **>= 2.61.0** | boolean | true |
| size | Size, providing three types of `large`, `medium`, and `small`, **>=1.11.0, currently only supports linear Tabs** | string | `large` |
Expand Down
53 changes: 47 additions & 6 deletions content/navigation/tabs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import { Tabs, TabPane } from '@douyinfe/semi-ui';

### 基本用法

标签栏支持三种样式的显示:线条式,按钮式,卡片式。默认选中第一项。
标签栏支持三种样式的显示:线条式,按钮式,卡片式,斜线式。默认选中第一项。
标签页支持两种传入方式,两者渲染流程上有所区别:

- 通过 `tabList` 传入标签页对象的数组,当使用 `tabList` 时每次只渲染当前传入的节点
Expand Down Expand Up @@ -153,6 +153,44 @@ class TabDemo extends React.Component {
}
```

```jsx live=true
import React from 'react';
import { Tabs } from '@douyinfe/semi-ui';

class TabDemo extends React.Component {
constructor() {
super();
this.state = { key: '1' };
this.onTabClick = this.onTabClick.bind(this);
}

onTabClick(key, type) {
this.setState({ [type]: key });
}

render() {
// eslint-disable-next-line react/jsx-key
const contentList = [<div>文档</div>, <div>快速起步</div>, <div>帮助</div>];
const tabList = [
{ tab: '文档', itemKey: '1' },
{ tab: '快速起步', itemKey: '2' },
{ tab: '帮助', itemKey: '3' },
];
return (
<Tabs
type="slash"
tabList={tabList}
onChange={key => {
this.onTabClick(key, 'key');
}}
>
{contentList[this.state.key - 1]}
</Tabs>
);
}
}
```

### 带图标的

有图标的标签栏。
Expand Down Expand Up @@ -257,7 +295,7 @@ function Demo() {

### 垂直的标签栏

支持水平和垂直两种模式, `tabPosition='left|top'`
`type``line`, `card`, `button` 支持水平和垂直两种模式,`tabPosition='left|top'`, 默认为 `top``type``slash` 仅支持水平模式,无需设置。

```jsx live=true
import React from 'react';
Expand Down Expand Up @@ -404,15 +442,17 @@ class App extends React.Component {

**自定义滚动箭头渲染**

通过 renderArrow 修改滚动折叠模式下,左右切换箭头的渲染,入参为溢出的 items 和 位置
通过 renderArrow 修改滚动折叠模式下,左右切换箭头的渲染,入参为溢出的 items 和 位置, 点击处理函数,以及 defaultNode。

****:renderArrow 的前三个参数自 2.61.0 支持,defaultNode 自 2.66.0 支持。

```jsx live=true dir="column"
import React from 'react';
import { Tabs, TabPane, Dropdown } from '@douyinfe/semi-ui';

() => {
const [activeKey, setActiveKey] = useState('Tab-0');
const renderArrow = (items, pos, handleArrowClick) => {
const renderArrow = (items, pos, handleArrowClick, defaultNode) => {
const style = {
width: 32,
height: 32,
Expand Down Expand Up @@ -718,14 +758,15 @@ class App extends React.Component {
| arrowPosition | 折叠模式下,左右切换箭头渲染位置 **>=2.61.0** | "start" "end" "both" ||
| className | 类名 | string ||
| collapsible | 折叠的 Tabs,**>=1.1.0** | boolean | false |
| dropdownProps | 用于在折叠模式下透传参数到下拉菜单的 Dropdown 组件 | { start: DropdownProps, end: DropdownProps } ||
| visibleTabsStyle | 整体滚动区域 Style **>=2.61.0** | style: CSSProperties ||
| contentStyle | 内容区域外层样式对象 | CSSProperties ||
| defaultActiveKey | 初始化选中的 tab 页的 key 值 | string | '1' |
| keepDOM | 使用 TabPane 写法时是否渲染隐藏面板的 DOM 结构 | boolean | true |
| lazyRender | 懒渲染,仅当面板激活过才被渲染在 DOM 树中 | boolean | false |
| more | 将一部分 Tab 渲染到下拉菜单中 ** >= 2.59.0** | number \| {count:number,render:()=>ReactNode,dropdownProps:DropDownProps} | - |
| renderTabBar | 用于二次封装标签栏 | (tabBarProps: object, defaultTabBar: React.ComponentType) => ReactNode ||
| renderArrow | 折叠滚动模式下,自定义左右切换箭头如何渲染,默认为箭头按钮 hover 时展开溢出项 **>=2.61.0** | (items: OverflowItem[],pos:"start"\|"end", handleArrowClick:()=>void)=> ReactNode ||
| renderArrow | 折叠滚动模式下,自定义左右切换箭头如何渲染,默认为箭头按钮 hover 时展开溢出项。前三个参数自 **>=2.61.0** 支持,defaultNode 参数自 **>=2.66.0** 支持| (items: OverflowItem[],pos:"start"\|"end", handleArrowClick:()=>void, defaultNode: ReactNode)=> ReactNode ||
| preventScroll | 指示浏览器是否应滚动文档以显示新聚焦的元素,作用于组件内的 focus 方法 | boolean | | |
| showRestInDropdown | 是否将收起的 Tab 展示在下拉菜单中(仅当 collapsible 为 true 时生效) **>= 2.61.0** | boolean | true |
| size | 大小,提供 `large``medium``small` 三种类型,**>=1.11.0,目前仅支持线性 Tabs** | string | `large` |
Expand All @@ -734,7 +775,7 @@ class App extends React.Component {
| tabList | 标签页对象组成的数组,该对象支持 itemKey(对应 activeKey,tab(标签页文字)及 icon(标签页图标) | TabPane[] ||
| tabPaneMotion | 是否使用动画切换 tabs | boolean | true |
| tabPosition | tab 的位置,支持`top`(水平), `left`(垂直) | string | `top` |
| type | 标签栏的样式,可选`line``card``button` | string | `line` |
| type | 标签栏的样式,可选`line``card``button``slash` | string | `line` |
| onChange | 切换 tab 页时的回调函数 | function(activeKey: string) ||
| onTabClick | 单击事件 | function(key: string, e: Event) ||
| onTabClose | 关闭 tab 页时的回调函数 **>=2.1.0** | function(tabKey: string) ||
Expand Down
10 changes: 5 additions & 5 deletions cypress/e2e/tabs.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,13 +74,13 @@ describe('tabs', () => {
cy.get('[id=semiTab1]').should('be.focused');
});

it('keyboard test when the tabs is closable', () => {
it.only('keyboard test when the tabs is closable', () => {
cy.visit('http://127.0.0.1:6006/iframe.html?id=tabs--tab-closable&args=&viewMode=story');
cy.get('[id=semiTab1]').click();
cy.get('[id=semiTab1]').should('be.focused');
cy.get('[data-tabkey=semiTab1]').eq(0).click();
cy.get('[data-tabkey=semiTab1]').eq(0).should('be.focused');

cy.get('[id=semiTab1]').type('{backspace}');
cy.get('[id=semiTab1]').should('not.exist');
cy.get('[data-tabkey=semiTab1]').eq(0).type('{backspace}');
cy.get('[data-tabkey=semiTab1]').should('not.exist');
});

it('collapsible', () => {
Expand Down
3 changes: 2 additions & 1 deletion packages/semi-foundation/tabs/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ const cssClasses = {
TABS_BAR_LINE: `${BASE_CLASS_PREFIX}-tabs-bar-line`,
TABS_BAR_CARD: `${BASE_CLASS_PREFIX}-tabs-bar-card`,
TABS_BAR_BUTTON: `${BASE_CLASS_PREFIX}-tabs-bar-button`,
TABS_BAR_SLASH: `${BASE_CLASS_PREFIX}-tabs-bar-slash`,
TABS_BAR_EXTRA: `${BASE_CLASS_PREFIX}-tabs-bar-extra`,
TABS_TAB: `${BASE_CLASS_PREFIX}-tabs-tab`,
TABS_TAB_ACTIVE: `${BASE_CLASS_PREFIX}-tabs-tab-active`,
Expand All @@ -29,7 +30,7 @@ const numbers = {
};

const strings = {
TYPE_MAP: ['line', 'card', 'button'],
TYPE_MAP: ['line', 'card', 'button', 'slash'],
SIZE: ['small', 'medium', 'large'],
POSITION_MAP: ['top', 'left']
};
Expand Down
42 changes: 42 additions & 0 deletions packages/semi-foundation/tabs/tabs.scss
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,10 @@ $ignoreIcon: '.#{$prefix}-icon-checkbox_tick, .#{$prefix}-icon-radio, .#{$prefix
color: var(--semi-color-text-2);
margin-left: 10px;
cursor: pointer;

&.#{$prefix}-icon-close:hover {
color: var(--semi-color-text-0);
}
}

&:hover {
Expand All @@ -182,6 +186,10 @@ $ignoreIcon: '.#{$prefix}-icon-checkbox_tick, .#{$prefix}-icon-radio, .#{$prefix
.#{$prefix}-icon:not(#{$ignoreIcon}) {
color: $color-tabs_tab-icon-hover;
}

.#{$prefix}-icon.#{$module}-tab-icon-close {
color: var(--semi-color-text-2);
}
}

&:active {
Expand All @@ -190,6 +198,10 @@ $ignoreIcon: '.#{$prefix}-icon-checkbox_tick, .#{$prefix}-icon-radio, .#{$prefix
.#{$prefix}-icon:not(#{$ignoreIcon}) {
color: $color-tabs_tab-icon-active;
}

.#{$prefix}-icon.#{$module}-tab-icon-close {
color: var(--semi-color-text-2);
}
}
}

Expand Down Expand Up @@ -583,6 +595,36 @@ $ignoreIcon: '.#{$prefix}-icon-checkbox_tick, .#{$prefix}-icon-radio, .#{$prefix
}
}

&-bar-slash {

.#{$module}-tab {
padding: $spacing-tabs_bar_slash_tab-paddingY $spacing-tabs_bar_slash_tab-paddingX;

&:nth-of-type(1) {
padding-left: 0;
}

&:not(:last-of-type) {
margin-right: $spacing-tabs_bar_slash-marginRight;

&:after {
content: "";
margin-left: $spacing-tabs_bar_slash_line_marginLeft;
display: inline-block;
height: $height-tabs_tab_slash_line;
width: $width-tabs_tab_slash_line;
margin-top: $spacing-tabs_bar_slash_line_marginTop;
margin-bottom: $spacing-tabs_bar_slash_line_marginBottom;
vertical-align: bottom;
// Get diagonal slash
background: linear-gradient(to bottom right, transparent 0%,
transparent calc(50% - 1px), $color-tabs_tab_slash_line 50%,
transparent calc(50% + 1px), transparent 100%);
}
}
}
}

&-content {
width: 100%;
padding: $spacing-tabs_content-paddingY $spacing-tabs_content-paddingX;
Expand Down
12 changes: 12 additions & 0 deletions packages/semi-foundation/tabs/variables.scss
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ $color-tabs_tab-pane_arrow_disabled-bg-hover: transparent;
$color-tabs_tab-pane_arrow_disabled-text-default: var(--semi-color-disabled-text);
$color-tabs_tab-pane_arrow_disabled-text-hover: var(--semi-color-disabled-text);

$color-tabs_tab_slash_line: var(--semi-color-text-2); //斜线式页签分割线颜色

$font-tabs_tab-fontWeight: $font-weight-regular; // 页签文本字重 - 默认
$font-tabs_tab_active-fontWeight: $font-weight-bold; // 页签文本字重 - 选中

Expand All @@ -79,6 +81,9 @@ $width-tabs-outline-offset: -2px; // 聚焦轮廓偏移宽度
$width-tabs_bar_line-outline-offset: -1px; // 线条式页签聚焦轮廓偏移宽度
$width-tabs_tab-pane_arrow-border:0px; // 滚动折叠箭头边框宽度

$width-tabs_tab_slash_line: 8px; // 斜线式页签分割线宽度
$height-tabs_tab_slash_line: 14px; // 斜线式页签分割线高度

$height-tabs_bar_extra_large: 50px; // 大尺寸页签高度
$font-tabs_bar_extra_large-lineHeight: $height-tabs_bar_extra_large; // 大尺寸页签文字行高

Expand Down Expand Up @@ -118,6 +123,13 @@ $spacing-tabs_bar_line_tab_left-padding: 12px; // 垂直线条式页签左侧内
$spacing-tabs_bar_line_tab_left_small-padding: $spacing-tight - 2px; // 小尺寸垂直线条式页签左侧内边距
$spacing-tabs_bar_line_tab_left_medium-padding: $spacing-base-tight - 2px; // 中等尺寸垂直线条式页签左侧内边距

$spacing-tabs_bar_slash_tab-paddingY: 12px; // 斜线式页签上下内边距
$spacing-tabs_bar_slash_tab-paddingX: 0px; // 斜线式页签水平内边距
$spacing-tabs_bar_slash-marginRight: 16px; // 斜线式页签右侧外边距
$spacing-tabs_bar_slash_line_marginLeft: 16px; // 斜线式页签斜线左侧外边距
$spacing-tabs_bar_slash_line_marginTop: 3px; // 斜线式页签斜线顶部外边距
$spacing-tabs_bar_slash_line_marginBottom: 3px; // 斜线式页签斜线底部外边距

$spacing-tabs_content-paddingY: 5px; // 页签内容区垂直方向内边距
$spacing-tabs_content-paddingX: 0; // 页签内容区水平方向内边距

Expand Down
13 changes: 9 additions & 4 deletions packages/semi-ui/tabs/TabBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ class TabBar extends React.Component<TabBarProps, TabBarState> {
</div>
);
}
const { dropdownClassName, dropdownStyle, showRestInDropdown } = this.props;
const { dropdownClassName, dropdownStyle, showRestInDropdown, dropdownProps } = this.props;
const { rePosKey } = this.state;
const disabled = !items.length;
const menu = (
Expand Down Expand Up @@ -197,6 +197,8 @@ class TabBar extends React.Component<TabBarProps, TabBarState> {
[`${cssClasses.TABS_BAR}-dropdown`]: true,
});

const customDropdownProps = dropdownProps?.[pos] ?? {};

return (
<>
{showRestInDropdown ? (
Expand All @@ -211,6 +213,7 @@ class TabBar extends React.Component<TabBarProps, TabBarState> {
style={dropdownStyle}
trigger={'hover'}
disableFocusListener // prevent the panel from popping up again after clicking
{...customDropdownProps}
>
{button}
</Dropdown>
Expand All @@ -221,11 +224,12 @@ class TabBar extends React.Component<TabBarProps, TabBarState> {

renderOverflow = (items: any[]): Array<ReactNode> => items.map((item, index) => {
const pos = index === 0 ? 'start' : 'end';
const icon = index === 0 ? <IconChevronLeft/> : <IconChevronRight/>;
const overflowNode = this.renderCollapse(item, icon, pos);
if (this.props.renderArrow) {
return this.props.renderArrow(item, pos, ()=>this.handleArrowClick(item, pos));
return this.props.renderArrow(item, pos, ()=>this.handleArrowClick(item, pos), overflowNode);
}
const icon = index === 0 ? <IconChevronLeft/> : <IconChevronRight/>;
return this.renderCollapse(item, icon, pos);
return overflowNode;
});


Expand Down Expand Up @@ -318,6 +322,7 @@ class TabBar extends React.Component<TabBarProps, TabBarState> {
[cssClasses.TABS_BAR_LINE]: type === 'line',
[cssClasses.TABS_BAR_CARD]: type === 'card',
[cssClasses.TABS_BAR_BUTTON]: type === 'button',
[cssClasses.TABS_BAR_SLASH]: type === 'slash',
[`${cssClasses.TABS_BAR}-${tabPosition}`]: tabPosition,
[`${cssClasses.TABS_BAR}-collapse`]: collapsible,
});
Expand Down
2 changes: 1 addition & 1 deletion packages/semi-ui/tabs/TabItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ const TabItem = (props: TabItemProps, ref: LegacyRef<HTMLDivElement>) => {
} = props;

const closableIcon = useMemo(() => {
return (type === 'card' && closable) ?
return closable ?
<IconClose
aria-label="Close"
role="button"
Expand Down
Loading

0 comments on commit b797aa1

Please sign in to comment.