🔨 refactor: Tab Common

This commit is contained in:
sonnguyenkieio
2021-09-09 15:42:19 +07:00
parent 6895539540
commit 717ff0fa27
6 changed files with 195 additions and 52 deletions

View File

@@ -1,12 +1,17 @@
@import '../../../styles/utilities';
.tabCommon { @import '../../../styles/utilities';
.tabWapper{
@apply flex flex-col w-full;
.tabHeader{
@apply flex;
.tabList {
@apply flex; @apply flex;
position: relative; position: relative;
border-bottom: 2px solid #FBFBFB; border-bottom: 2px solid #FBFBFB;
padding-top: 1.6rem; padding: 0.8rem 0;
padding-bottom: 1.6rem; &.center{
width: 100%; margin: auto;
}
.slider { .slider {
@apply inline-block; @apply inline-block;
@@ -16,7 +21,8 @@
position: absolute; position: absolute;
z-index: 1200; z-index: 1200;
bottom: 0; bottom: 0;
transition: all .4s linear; transition: all .25s linear;
}
}
} }
} }

View File

@@ -1,36 +1,85 @@
import React, { RefObject, useEffect } from "react" import React, {
Children,
PropsWithChildren,
ReactElement,
useEffect,
useRef,
useState,
cloneElement,
} from 'react'
import s from './TabCommon.module.scss' import s from './TabCommon.module.scss'
import TabItem from './TabItem/TabItem' import TabItem from './components/TabItem/TabItem'
import { TabPaneProps } from './components/TabPane/TabPane'
import classNames from 'classnames'
interface TabCommonProps { interface TabCommonProps {
tabs: {ref:RefObject<HTMLLIElement>, tabName: string, active: boolean, onClick: (tabIndex: number, tabPane?: string) => void}[]; defaultActiveTab?: number
defaultActiveTab: number; children: React.ReactNode
sliderRef : RefObject<HTMLDivElement>; center?:boolean
slideToTab: (ref: any) => void;
} }
const TabCommon = ({ tabs, defaultActiveTab, sliderRef, slideToTab } : TabCommonProps) => { const TabCommon = ({
defaultActiveTab = 0,
children,
center
}: TabCommonProps) => {
const [active, setActive] = useState(0)
const slider = useRef<HTMLDivElement>(null)
const headerRef = useRef<HTMLUListElement>(null)
useEffect(() => { useEffect(() => {
slideToTab(tabs[defaultActiveTab].ref); setActive(defaultActiveTab)
}, []) }, [])
useEffect(() => {
slide(active)
}, [active])
function slide(index: number) {
const active = headerRef.current?.children
.item(index)
?.getBoundingClientRect()
const header = headerRef.current?.getBoundingClientRect()
const current = slider.current
if (current && active && header) {
let width = active.width - 24 <= 0 ? 24 : active.width - 24
let left = active.left - header.left
current.style.width = width.toString() + 'px'
current.style.left = left.toString() + 'px'
}
}
const onTabClick = (index: number) => {
setActive(index)
}
return ( return (
<ul className={s.tabCommon}> <section className={s.tabWapper}>
{ <div className={s.tabHeader}>
tabs.map((tab) => { <ul className={classNames(s.tabList,{[s.center]:center})} ref={headerRef}>
{Children.map(children, (tab, index) => {
let item = tab as ReactElement<PropsWithChildren<TabPaneProps>>
return ( return (
<li key={tab.tabName} ref={tab.ref}> <li key={item.props.tabName}>
<TabItem onClick={tab.onClick} active={tab.active}>{tab.tabName}</TabItem> <TabItem
active={active === index}
onClick={onTabClick}
tabIndex={index}
>
{item.props.tabName}
</TabItem>
</li> </li>
) )
}) })}
} <div ref={slider} className={s.slider}></div>
<div ref={sliderRef} className={s.slider}></div>
</ul> </ul>
</div>
<div className={s.tabBody}>
{Children.map(children, (tab, index) => {
let item = tab as ReactElement<PropsWithChildren<TabPaneProps>>
return cloneElement(item, { active:index===active });
})
}</div>
</section>
) )
} }
export default TabCommon; export default TabCommon

View File

@@ -0,0 +1,13 @@
@import '../../../../../styles/utilities';
.tabItem {
margin-right:2.4rem;
padding: 0.8rem 0;
min-width: 2.4rem;
&:hover {
@apply cursor-pointer;
}
&.tabItemActive {
@apply font-bold;
}
}

View File

@@ -0,0 +1,31 @@
import classNames from 'classnames'
import React from 'react'
import s from './TabItem.module.scss'
interface TabItemProps {
active: boolean
children: string
onClick?: (tabIndex: number) => void
tabIndex: number
}
const TabItem = ({
active = false,
children,
onClick,
tabIndex,
}: TabItemProps) => {
const handleClick = () => {
onClick && onClick(tabIndex)
}
return (
<span
onClick={handleClick}
className={classNames(s.tabItem, {[s.tabItemActive]:active})}
>
{children}
</span>
)
}
export default TabItem

View File

@@ -0,0 +1,23 @@
@import "../../../../../styles/utilities";
.tabPane {
@apply hidden;
transition: all 0.6s;
// animation-duration: 0.6s;
// animation-name: appear;
// @keyframes appear {
// from {
// margin-left: 100%;
// width: 200%;
// }
// to {
// margin-left: 0%;
// width: 100%;
// }
// }
&.active {
@apply block;
}
}

View File

@@ -0,0 +1,21 @@
import classNames from "classnames"
import React from "react"
import s from './TabPane.module.scss'
export interface TabPaneProps {
active?: boolean;
children?: React.ReactNode;
tabName: string
}
const TabPane = ({ active, children } : TabPaneProps) => {
return (
<section className={classNames(s.tabPane, {
[s.active] : active
})}>
{children}
</section>
)
}
export default TabPane