Skip to content

Commit

Permalink
Merge pull request #874 from hxtree/feature/tabs
Browse files Browse the repository at this point in the history
Add Tabs component
  • Loading branch information
hxtree authored Mar 21, 2024
2 parents 585a825 + 32b161b commit e68a820
Show file tree
Hide file tree
Showing 7 changed files with 134 additions and 2 deletions.
4 changes: 2 additions & 2 deletions clients/design-system/src/components/AppBar/style.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
}
}

.app-bar{
.app-bar {
background-color: $color-white;
.navbar-menu {
box-shadow: rgba(0, 0, 0, 0.12) 0px 4px 30px 0px
Expand All @@ -25,7 +25,6 @@
}
}


.app-bar-light {
padding: 0;
background-color: rgb(255, 253, 251);
Expand Down Expand Up @@ -57,6 +56,7 @@
}
}
}

.app-bar-dark {
background-color: $color-gray !important;
}
71 changes: 71 additions & 0 deletions clients/design-system/src/components/Tabs/Tabs.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import React, { useState, useRef } from 'react';
import { TabsProps, Tab } from './Tabs.type';
import { faChevronCircleLeft, faChevronCircleRight } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import './style.module.scss';

export const Tabs: React.FC<TabsProps> = ({ tabs }) => {
const [activeTab, setActiveTab] = useState<number>(0);
const tabRefs = useRef<HTMLButtonElement[]>([]);
const tabWindowRef = useRef<HTMLDivElement>(null);
const handleClick = (index: number) => {
setActiveTab(index);
};

const handlePrevious = () => {
setActiveTab((prevActiveTab: number) => (prevActiveTab === 0 ? prevActiveTab : prevActiveTab - 1));
const previousTabParent = tabRefs.current[activeTab - 1].parentElement;
const elementPosition = previousTabParent?.getBoundingClientRect()
const positionX = elementPosition?.left ?? 0;
tabWindowRef.current?.scrollTo({left: positionX, behavior: 'smooth'})
}

const handleNext = () => {
setActiveTab((prevActiveTab: number) => (prevActiveTab === tabs.length - 1 ? prevActiveTab : prevActiveTab + 1));
const nextTabParent = tabRefs.current[activeTab].parentElement;
const elementPosition = nextTabParent?.getBoundingClientRect()
const positionX = elementPosition?.right ?? 0;
tabWindowRef.current?.scrollTo({left: positionX, behavior: 'smooth'})
};

return (
<div className="tabs">
<div ref={tabWindowRef} className={'scrollable'}>
<ul className="nav nav-tabs flex-nowrap">
{tabs.map((tab: Tab, index: number) => (
<li className="nav-item" key={index}>
<button
className={`nav-link mx-2 ${index === activeTab ? 'active' : ''}`}
onClick={() => handleClick(index)}
ref={(ref) => (tabRefs.current[index] = ref as HTMLButtonElement)}
>
{tab.title}
</button>
</li>
))}
</ul>
</div>
<div className="tab-content">
{tabs.map((tab: Tab, index: number) => (
<div
className={`tab-pane fade ${index === activeTab ? 'show active' : ''}`}
key={index}
>
{tab.content}
</div>
))}
</div>
<div className="d-flex justify-content-center mt-3">
<button className="btn btn-link me-2" onClick={handlePrevious} disabled={activeTab === 0}>
<FontAwesomeIcon size='2x' icon={faChevronCircleLeft}/>
</button>
<span>{`${activeTab + 1}/${tabs.length}`}</span>
<button className="btn btn-link ms-2" onClick={handleNext} disabled={activeTab === tabs.length - 1}>
<FontAwesomeIcon size='2x' icon={faChevronCircleRight}/>
</button>
</div>
</div>
);
};

export default Tabs;
8 changes: 8 additions & 0 deletions clients/design-system/src/components/Tabs/Tabs.type.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export type Tab = {
title: JSX.Element;
content: JSX.Element;
};

export type TabsProps = {
tabs: Tab[];
};
14 changes: 14 additions & 0 deletions clients/design-system/src/components/Tabs/style.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@

.tabs {
.scrollable {
&::-webkit-scrollbar {
display: none;
}
scrollbar-width: none;
-ms-overflow-style: none;
max-width: 100%;
overflow-x: scroll;
white-space: nowrap;
--webkit-overflow-scrolling: touch;
}
}
2 changes: 2 additions & 0 deletions clients/design-system/src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,5 @@ export * from './Paper/Paper';
export * from './Hero/Hero';
export * from './Images/Images';
export * from './BlurbPair/BlurbPair';
export * from './Tabs/Tabs';
export * from './Tabs/Tabs.type';
1 change: 1 addition & 0 deletions clients/design-system/src/index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ $success: #7952b3;
@import './components/Hero/style.module.scss';
@import './components/Card/style.module.scss';
@import './components/BlurbPair/style.module.scss';
@import './components/Tabs/style.module.scss';

body {
font-family: $font-family-sans-serif;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import React from 'react';
import { Meta } from '@storybook/react';
import { FontAwesomeIcon, Tabs, TabsProps, faAddressCard, faBars, faBook, faCheck, faCog, faGithub } from '../../../src/main';

export default {
title: 'Molecules/Tab',
component: Tabs,
argTypes: {},
} as Meta<typeof Tabs>;

const args: TabsProps = {
tabs: [
{
title: <FontAwesomeIcon size='4x' icon={faCheck}/>,
content: <p>Tab 1 content</p>,
},
{
title: <FontAwesomeIcon size='4x' icon={faBook}/>,
content: <p>Tab 2 content</p>,
},
{
title: <FontAwesomeIcon size='4x' icon={faGithub}/>,
content: <p>Tab 3 content</p>,
},
{
title: <FontAwesomeIcon size='4x' icon={faAddressCard}/>,
content: <p>Tab 4 content</p>,
},
{
title: <FontAwesomeIcon size='4x' icon={faBars}/>,
content: <p>Tab 5 content</p>,
},
],
}

export const Default = () => <Tabs {...args}/>

0 comments on commit e68a820

Please sign in to comment.