diff --git a/packages/react-form/src/style/form-item.less b/packages/react-form/src/style/form-item.less index d2d0a0d151..1948d1b741 100644 --- a/packages/react-form/src/style/form-item.less +++ b/packages/react-form/src/style/form-item.less @@ -10,6 +10,10 @@ color: #dc3545; } .w-input-inner, + .w-select, + .w-select-default, + .w-select:hover, + .w-select-default:hover, .w-input-inner:hover, .w-input-inner:focus.w-input-inner:hover, .w-textarea, diff --git a/packages/react-tabs/README.md b/packages/react-tabs/README.md index 05fb130aca..c4eb458f34 100644 --- a/packages/react-tabs/README.md +++ b/packages/react-tabs/README.md @@ -146,7 +146,45 @@ class Demo extends React.Component { ReactDOM.render(, _mount_); ``` -## Tabs.Porps +### 超出收缩 + +当pane过多,超出宽度度时,会将超出部分收缩到下拉选项 + + +```jsx +import React from 'react'; +import ReactDOM from 'react-dom'; +import { Tabs } from 'uiw'; + +class Demo extends React.Component { + +render() { + const panes=[] + for (let index = 0; index < 20; index++) { + panes.push({label:`Tabs-${index}`,key:index}) + } + + return ( + { + console.log("=>", key, tab); + }}> + {panes.map(item=>{ + return( + {item.label} + ) + })} + + ); + } +} +ReactDOM.render(, _mount_); +``` + + +## Tabs.Porps | 参数 | 说明 | 类型 | 默认值 | |------ |-------- |---------- |-------- | @@ -154,10 +192,10 @@ ReactDOM.render(, _mount_); | activeKey | 当前激活 `tab` 面板的 `key` | String | - | | onTabClick | `tab` 被点击的回调 | Function | `(item, key, e)=>{}` | -## Tabs.Pane.Porps +## Tabs.Pane.Porps | 参数 | 说明 | 类型 | 默认值 | |------ |-------- |---------- |-------- | | label | 选项卡标题 | String,Node | - | | key | 对应 activeKey | String,Node | - | -| disabled | 标签是禁用不可点击 | Boolean | `false` | \ No newline at end of file +| disabled | 标签是禁用不可点击 | Boolean | `false` | diff --git a/packages/react-tabs/package.json b/packages/react-tabs/package.json index 5238248575..28a7d43871 100644 --- a/packages/react-tabs/package.json +++ b/packages/react-tabs/package.json @@ -44,6 +44,7 @@ "react-dom": ">=16.9.0" }, "dependencies": { + "@uiw/react-popover": "^4.13.7", "@uiw/utils": "^4.13.7" } } diff --git a/packages/react-tabs/src/index.tsx b/packages/react-tabs/src/index.tsx index 1639921ee3..de92ac40a0 100644 --- a/packages/react-tabs/src/index.tsx +++ b/packages/react-tabs/src/index.tsx @@ -1,7 +1,8 @@ -import React, { useEffect, useState, useRef } from 'react'; +import React, { useEffect, useState, useRef, useCallback, useMemo } from 'react'; import { IProps, HTMLDivProps } from '@uiw/utils'; import Pane from './Pane'; import './style/index.less'; +import Popover from '@uiw/react-popover'; export * from './Pane'; @@ -15,6 +16,14 @@ export interface TabsProps extends IProps, HTMLDivProps { onTabClick?: (key: string, item: React.ReactElement, e: React.MouseEvent) => void; } +type FlowNavType = { + content: number; + nav: Array>; + flowLeft: number; + displayStart: number; + displayEnd: number; +}; + export default function Tabs(props: TabsProps) { const { prefixCls = 'w-tabs', @@ -25,11 +34,70 @@ export default function Tabs(props: TabsProps) { onTabClick, ...elementProps } = props; + const [activeKey, setActiveKey] = useState(props.activeKey); const [slideStyle, setSlideStyle] = useState({ width: 0, left: 0 }); const activeItem = useRef(); const cls = [prefixCls, className, type ? `${prefixCls}-${type}` : null].filter(Boolean).join(' ').trim(); + const [flowNav, flowNavSet] = useState({ + content: 0, + nav: [], + flowLeft: -1, + displayStart: 0, + displayEnd: 0, + }); + const [hiddenNav, hiddenNavSet] = useState>([]); + const deviation = 15; + + const [nodes, nodesSet] = useState(); + const divContentRef = useCallback((node) => { + if (node !== null) { + nodesSet(nodes); + node.addEventListener('scroll', (e: any) => { + const { clientWidth, scrollLeft } = e.target; + flowNav.displayStart = scrollLeft; + flowNav.displayEnd = clientWidth + scrollLeft; + flowNavSet({ ...flowNav }); + }); + flowNav.displayEnd = node.getBoundingClientRect().width; + flowNavSet({ ...flowNav }); + } + }, []); + + const divNavRef = useCallback((node, key: number) => { + if (node !== null) { + node.addEventListener('click', (e: any) => { + activeItem.current = node; + }); + divNavWidthChange(node.getBoundingClientRect().width, key); + } + }, []); + + const divNavWidthChange = (width: number, index: number) => { + let curWidth = 0; + flowNav.nav.slice(0, index + 1).forEach((nav) => (curWidth += nav.width)); + flowNav.nav[index] = { width, curWidth: Math.floor(curWidth), index }; + flowNavSet(flowNav); + }; + + useEffect(() => { + showHideenNav(); + }, [flowNav.displayEnd > flowNav.nav[flowNav.nav.length - 1]?.curWidth]); + + const showHideenNav = () => { + const hiddenNav: Array = []; + if (flowNav.nav.length > 0) { + flowNav.nav.forEach((item) => { + const curWidth = item.curWidth - deviation; + if (curWidth < flowNav.displayStart || curWidth > flowNav.displayEnd) { + hiddenNav.push(item.index); + } + }); + hiddenNavSet(hiddenNav); + } + }; + useEffect(() => setActiveKey(props.activeKey), [props.activeKey]); useEffect(() => calcSlideStyle(), [activeKey]); @@ -44,44 +112,31 @@ export default function Tabs(props: TabsProps) { return (
-
-
- {React.Children.map(children as React.ReactElement[], (item: React.ReactElement, key: number) => { - if (!item) { - return null; - } - const divProps: HTMLDivProps = { - className: [ - `${prefixCls}-item`, - item.key === activeKey ? 'active' : null, - item.props.disabled ? 'disabled' : null, - ] - .filter(Boolean) - .join(' ') - .trim(), - children: item.props.label, - }; - if (!item.props.disabled) { - divProps.onClick = (e: React.MouseEvent) => { - setActiveKey(item.key as string); - onTabClick && onTabClick(item.key as string, item, e); - calcSlideStyle(); - }; - } - return ( -
{ - if (node && item.key === activeKey) { - activeItem.current = node; - } - }} - {...divProps} - /> - ); - })} +
+
+
+
+ {renderNav(children)} +
+
+ {hiddenNav.length > 0 && ( + + {renderNav(hiddenNav.map((idx) => (children as Array)[idx]))} +
+ } + > +
+ +
+ + )}
{React.Children.map(children, (item: any) => { if (!item || activeKey !== item.key) { @@ -91,4 +146,31 @@ export default function Tabs(props: TabsProps) { })}
); + + function renderNav(children: React.ReactNode): React.ReactNode { + return React.Children.map(children as React.ReactElement[], (item: React.ReactElement, key: number) => { + if (!item) { + return null; + } + const divProps: HTMLDivProps = { + className: [ + `${prefixCls}-item`, + item.key === activeKey ? 'active' : null, + item.props.disabled ? 'disabled' : null, + ] + .filter(Boolean) + .join(' ') + .trim(), + children: item.props.label, + }; + if (!item.props.disabled) { + divProps.onClick = (e: React.MouseEvent) => { + setActiveKey(item.key as string); + onTabClick && onTabClick(item.key as string, item, e); + calcSlideStyle(); + }; + } + return
divNavRef(ref, key)} {...divProps} />; + }); + } } diff --git a/packages/react-tabs/src/style/index.less b/packages/react-tabs/src/style/index.less index 09ab8a4feb..6c884d76fb 100644 --- a/packages/react-tabs/src/style/index.less +++ b/packages/react-tabs/src/style/index.less @@ -3,10 +3,19 @@ .@{w-tabs} { &-bar { position: relative; + overflow-x: auto; + height: calc(100% + 17px); } &-nav { position: relative; } + &-nav-hidden { + display: flex; + overflow-x: scroll; + padding: 5px 30px 5px 5px; + max-height: 200px; + flex-direction: column; + } &-item { padding: 7px 10px; display: inline-block; @@ -26,6 +35,12 @@ color: rgba(0, 0, 0, 0.25); } } + &-flow-content { + margin-left: 5px; + padding: 0px 10px 0px 10px; + box-shadow: 1px 0px 0px #d9d9d9 inset; + cursor: pointer; + } } .@{w-tabs} {