Skip to content

Commit

Permalink
feat: Tabs add slash type, renderArrow add defaultNode params, closab…
Browse files Browse the repository at this point in the history
…le allow all types
  • Loading branch information
YyumeiZhang committed Aug 16, 2024
1 parent ef8a3a5 commit 6bf59d3
Show file tree
Hide file tree
Showing 9 changed files with 155 additions and 18 deletions.
6 changes: 4 additions & 2 deletions content/navigation/tabs/index-en-US.md
Original file line number Diff line number Diff line change
Expand Up @@ -381,7 +381,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.65.0.
```jsx live=true dir="column"
import React from 'react';
Expand Down Expand Up @@ -708,7 +710,7 @@ class App extends React.Component {
| 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.65.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
8 changes: 5 additions & 3 deletions content/navigation/tabs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -404,15 +404,17 @@ class App extends React.Component {

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

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

****:renderArrow 的前三个参数自 2.61.0 支持,defaultNode 自 2.65.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 @@ -725,7 +727,7 @@ class App extends React.Component {
| 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.65.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 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
34 changes: 34 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,28 @@ $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: "/";
font-weight: $font-tabs_tab_slash_line-fontWeight;
margin-left: $spacing-tabs_bar_slash_line_marginLeft;
color: $color-tabs_tab_slash_line;
}
}
}
}

&-content {
width: 100%;
padding: $spacing-tabs_content-paddingY $spacing-tabs_content-paddingX;
Expand Down
8 changes: 8 additions & 0 deletions packages/semi-foundation/tabs/variables.scss
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@ $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_slash_line-fontWeight: $font-weight-regular; //斜线式页签分割线字重

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

Expand Down Expand Up @@ -118,6 +121,11 @@ $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_content-paddingY: 5px; // 页签内容区垂直方向内边距
$spacing-tabs_content-paddingX: 0; // 页签内容区水平方向内边距

Expand Down
8 changes: 5 additions & 3 deletions packages/semi-ui/tabs/TabBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -221,11 +221,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 +319,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
100 changes: 94 additions & 6 deletions packages/semi-ui/tabs/_story/tabs.stories.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useState } from 'react';
import React, { useState, useCallback } from 'react';
import Tabs from '../index';
import Button from '@douyinfe/semi-ui/button/index';
import Typography from '@douyinfe/semi-ui/typography/index';
Expand Down Expand Up @@ -363,12 +363,42 @@ Level2Card.story = {
name: 'Level 2-卡片Tab',
};

export const SlashTab = () => {
return (
<>
<Tabs defaultActiveKey="1" type="slash" >
<TabPane tab="文档" itemKey="1">文档</TabPane>
<TabPane tab="快速起步" itemKey="2" disabled>快速起步</TabPane>
<TabPane tab="帮助" itemKey="3">帮助</TabPane>
<TabPane tab="关于" itemKey="4">关于</TabPane>
<TabPane tab="资源工具" itemKey="5">资源工具</TabPane>
</Tabs>
<br />
<br />
<Tabs defaultActiveKey="1" type="slash">
<TabPane tab="文档" itemKey="1">文档</TabPane>
<TabPane tab="快速起步" itemKey="2" disabled>快速起步</TabPane>
<TabPane tab="帮助" itemKey="3">帮助</TabPane>
<TabPane tab="关于" itemKey="4">关于</TabPane>
<TabPane tab="资源工具" itemKey="5">资源工具</TabPane>
</Tabs>
<br />
<Tabs style={{ width: '400px'}} type="slash" collapsible>
{['文档', "快速起步", "帮助", "关于", "资源工具"].map((i, index) => (
<TabPane tab={`tab-${index}`} itemKey={i} key={i} >
Content of card tab {i}
</TabPane>
))}
</Tabs>
</>)
}

export const Level3ButtonTab = () => (
<Tabs style={style} defaultActiveKey="1" type="button">
<TabPane tab="文档" itemKey="1">
文档
</TabPane>
<TabPane tab="快速起步" itemKey="2">
<TabPane tab="快速起步" itemKey="2" disabled>
快速起步
</TabPane>
<TabPane tab="帮助" itemKey="3">
Expand Down Expand Up @@ -889,11 +919,35 @@ class TabClosableDemo extends React.Component {

render() {
return (
<Tabs type="card" defaultActiveKey="1" onTabClose={this.close.bind(this)}>
<>
<br />
<Tabs type="card" defaultActiveKey="1" onTabClose={this.close.bind(this)}>
{
this.state.tabList.map(t=><TabPane closable={t.closable} tab={t.tab} itemKey={t.itemKey} key={t.itemKey}>{t.text}</TabPane>)
this.state.tabList.map(t=><TabPane closable={t.closable} tab={t.tab} itemKey={t.itemKey} key={t.itemKey} >{t.text}</TabPane>)
}
</Tabs>
</Tabs>
<br />
<br />
<Tabs type="line" defaultActiveKey="1" onTabClose={this.close.bind(this)}>
{
this.state.tabList.map(t=><TabPane closable={t.closable} tab={t.tab} itemKey={t.itemKey} key={t.itemKey} >{t.text}</TabPane>)
}
</Tabs>
<br />
<br />
<Tabs type="button" defaultActiveKey="1" onTabClose={this.close.bind(this)}>
{
this.state.tabList.map(t=><TabPane closable={t.closable} tab={t.tab} itemKey={t.itemKey} key={t.itemKey} >{t.text}</TabPane>)
}
</Tabs>
<br />
<br />
<Tabs type="slash" defaultActiveKey="1" onTabClose={this.close.bind(this)}>
{
this.state.tabList.map(t=><TabPane closable={t.closable} tab={t.tab} itemKey={t.itemKey} key={t.itemKey} >{t.text}</TabPane>)
}
</Tabs>
</>
);
}
}
Expand Down Expand Up @@ -1058,4 +1112,38 @@ export const ShowRestInDropdownDemo = () => {
))}
</Tabs>
)
}
}

export const DynamicShowArrow = () => {
const [hidden, setHidden] = useState(true);
const renderArrow = (items, pos, handleArrowClick, defaultNode) => {
const style = { visibility: hidden ? 'hidden': 'visible'};
return <span style={style}>
{defaultNode}
</span>
};
const onVisibleTabsChange = useCallback((visibleState) => {
let values = Object.values(Object.fromEntries(visibleState));
if (values.includes(false)) {
setHidden(false);
} else {
setHidden(true);
}
}, []);
return (
<Tabs
renderArrow={renderArrow}
style={{ margin: '20px' }}
type="line"
arrowPosition={"end"}
collapsible
onVisibleTabsChange={onVisibleTabsChange}
>
{[0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(i => (
<TabPane tab={`Tab-${i}`} itemKey={`Tab-${i}`} key={i}>
Content of card tab {i}
</TabPane>
))}
</Tabs>
);
};
4 changes: 2 additions & 2 deletions packages/semi-ui/tabs/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import TabBar, { OverflowItem } from './TabBar';
import { DropdownProps } from "../dropdown";
import { OverflowListProps } from "../overflowList";

export type TabType = 'line' | 'card' | 'button';
export type TabType = 'line' | 'card' | 'button' | 'slash';
export type TabSize = 'small' | 'medium' | 'large';
export type TabPosition = 'top' | 'left';

Expand Down Expand Up @@ -69,7 +69,7 @@ export interface TabBarProps {
onVisibleTabsChange?: (visibleState: Map<string, boolean>) => void;
visibleTabsStyle?: CSSProperties;
arrowPosition?: OverflowListProps['overflowRenderDirection'];
renderArrow?: (items: OverflowItem[], pos: "start"|"end", handleArrowClick: () => void) => ReactNode
renderArrow?: (items: OverflowItem[], pos: "start"|"end", handleArrowClick: () => void, defaultNode: ReactNode) => ReactNode

}

Expand Down

0 comments on commit 6bf59d3

Please sign in to comment.