Merge branch 'dev' into staging
This commit is contained in:
commit
6cdac148b4
@ -1,5 +1,5 @@
|
||||
.root {
|
||||
.hidden-tester {
|
||||
.mirror-shadow-element {
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
background: red;
|
||||
@ -19,16 +19,6 @@
|
||||
.show-more-container {
|
||||
position: relative;
|
||||
border-bottom: 1px solid var(--color-neutral-500);
|
||||
.show-more {
|
||||
padding: 8px 16px;
|
||||
display: flex;
|
||||
color: white;
|
||||
font-size: 16px;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
color: white;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.vertical-container {
|
||||
position: absolute;
|
||||
@ -46,4 +36,15 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.show-more {
|
||||
padding: 8px 16px;
|
||||
display: flex;
|
||||
color: white;
|
||||
font-size: 16px;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
color: white;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ import classes from "./classes.module.scss";
|
||||
import HorizontalTab, { ITab } from "./HorizontalTab";
|
||||
import VerticalTabs from "./VerticalTabs";
|
||||
import Typography, { ETypo, ETypoColor } from "@Front/Components/DesignSystem/Typography";
|
||||
import { useDebounce, useWindowSize } from "@uidotdev/usehooks";
|
||||
import { useWindowSize } from "@uidotdev/usehooks";
|
||||
import useOpenable from "@Front/Hooks/useOpenable";
|
||||
|
||||
export type ITabValue<T> = T & {
|
||||
@ -20,57 +20,80 @@ type IProps<T> = {
|
||||
onSelect: (value: T) => void;
|
||||
};
|
||||
|
||||
export default function Tabs<T>(props: IProps<T>) {
|
||||
const { onSelect } = props;
|
||||
const rootRef = useRef<HTMLDivElement>(null);
|
||||
export default function Tabs<T>({ onSelect, tabs: propsTabs }: IProps<T>) {
|
||||
const tabs = useRef(propsTabs);
|
||||
|
||||
const shadowElementRef = useRef<HTMLDivElement>(null);
|
||||
const [visibleElements, setVisibleElements] = useState<ITabInternal<T>[]>([]);
|
||||
const [overflowedElements, setOverflowedElements] = useState<ITabInternal<T>[]>([]);
|
||||
|
||||
const [selectedTab, setSelectedTab] = useState<ITabValue<T>>(props.tabs[0]!.value);
|
||||
const [selectedTab, setSelectedTab] = useState<ITabValue<T>>(tabs.current[0]!.value);
|
||||
|
||||
const { close, isOpen, toggle } = useOpenable();
|
||||
|
||||
const windowSize = useWindowSize();
|
||||
const windowSizeDebounced = useDebounce(windowSize, 100);
|
||||
|
||||
const calculateVisibleElements = useCallback(() => {
|
||||
const container = rootRef.current;
|
||||
if (!container) return;
|
||||
const shadowElement = shadowElementRef.current;
|
||||
if (!shadowElement) return;
|
||||
|
||||
const shadowElementWidth = shadowElement.offsetWidth;
|
||||
// The first element is the show more element, it needs to be ignored in the calculation
|
||||
let totalWidth = (shadowElement.children[0]! as HTMLElement).offsetWidth;
|
||||
|
||||
const containerWidth = container.offsetWidth;
|
||||
let totalWidth = 115;
|
||||
let visibleCount = 0;
|
||||
|
||||
const children = Array.from(container.children) as HTMLDivElement[];
|
||||
for (let i = 0; i < children.length; i++) {
|
||||
totalWidth += children[i]!.offsetWidth;
|
||||
if (totalWidth > containerWidth) {
|
||||
for (let i = 1; i < shadowElement.children.length; i++) {
|
||||
totalWidth += (shadowElement.children[i]! as HTMLElement).offsetWidth;
|
||||
if (totalWidth > shadowElementWidth) {
|
||||
break;
|
||||
}
|
||||
visibleCount++;
|
||||
}
|
||||
|
||||
setVisibleElements(props.tabs.slice(0, visibleCount));
|
||||
setOverflowedElements(props.tabs.slice(visibleCount));
|
||||
}, [props.tabs]);
|
||||
setVisibleElements(tabs.current.slice(0, visibleCount));
|
||||
setOverflowedElements(tabs.current.slice(visibleCount));
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
tabs.current = propsTabs;
|
||||
}, [propsTabs]);
|
||||
|
||||
useEffect(() => {
|
||||
calculateVisibleElements();
|
||||
}, [calculateVisibleElements, windowSizeDebounced]);
|
||||
}, [calculateVisibleElements, windowSize]);
|
||||
|
||||
const handleSelect = useCallback(
|
||||
(value: ITabValue<T>) => {
|
||||
setSelectedTab(value);
|
||||
onSelect(value);
|
||||
close();
|
||||
calculateVisibleElements();
|
||||
},
|
||||
[close, onSelect],
|
||||
[close, onSelect, calculateVisibleElements],
|
||||
);
|
||||
|
||||
const handleVerticalSelect = useCallback(
|
||||
(value: ITabValue<T>) => {
|
||||
const index = tabs.current.findIndex((tab) => tab.value.id === value.id);
|
||||
const newTabs = [...tabs.current];
|
||||
newTabs.splice(index, 1);
|
||||
newTabs.unshift(tabs.current[index]!);
|
||||
tabs.current = newTabs;
|
||||
console.log("Updated values ; ", tabs.current);
|
||||
handleSelect(value);
|
||||
},
|
||||
[handleSelect],
|
||||
);
|
||||
|
||||
return (
|
||||
<div className={classes["root"]}>
|
||||
<div className={classes["hidden-tester"]} ref={rootRef}>
|
||||
{props.tabs.map((element, index) => (
|
||||
<div className={classes["mirror-shadow-element"]} ref={shadowElementRef}>
|
||||
<div className={classes["show-more"]}>
|
||||
<Typography typo={ETypo.TEXT_MD_REGULAR} color={ETypoColor.COLOR_NEUTRAL_500}>
|
||||
{overflowedElements.length} de plus...
|
||||
</Typography>
|
||||
</div>
|
||||
{tabs.current.map((element, index) => (
|
||||
<HorizontalTab<T>
|
||||
label={element.label}
|
||||
key={element.key ?? index}
|
||||
@ -106,7 +129,7 @@ export default function Tabs<T>(props: IProps<T>) {
|
||||
label={element.label}
|
||||
key={element.key ?? index}
|
||||
value={element.value}
|
||||
onSelect={handleSelect}
|
||||
onSelect={handleVerticalSelect}
|
||||
isSelected={selectedTab === element.value}
|
||||
/>
|
||||
))}
|
||||
|
@ -8,10 +8,11 @@ import DefaultTemplate from "@Front/Components/LayoutTemplates/DefaultTemplate";
|
||||
|
||||
import classes from "./classes.module.scss";
|
||||
import Tabs from "@Front/Components/Elements/Tabs";
|
||||
import { useCallback, useState } from "react";
|
||||
import { useCallback, useMemo, useState } from "react";
|
||||
|
||||
export default function DesignSystem() {
|
||||
const userDb = [
|
||||
const userDb = useMemo(
|
||||
() => [
|
||||
{
|
||||
username: "Maxime",
|
||||
id: 1,
|
||||
@ -32,7 +33,19 @@ export default function DesignSystem() {
|
||||
username: "Arnaud",
|
||||
id: 5,
|
||||
},
|
||||
];
|
||||
],
|
||||
[],
|
||||
);
|
||||
|
||||
const userDbArray = useMemo(
|
||||
() =>
|
||||
userDb.map((user) => ({
|
||||
label: user.username,
|
||||
key: user.id.toString(),
|
||||
value: user,
|
||||
})),
|
||||
[userDb],
|
||||
);
|
||||
|
||||
const [selectedTab, setSelectedTab] = useState<(typeof userDb)[number]>(userDb[0]!);
|
||||
|
||||
@ -45,14 +58,7 @@ export default function DesignSystem() {
|
||||
<Typography typo={ETypo.DISPLAY_LARGE}>DesignSystem</Typography>
|
||||
<Newsletter isOpen />
|
||||
<Typography typo={ETypo.TEXT_LG_BOLD}>Tabs</Typography>
|
||||
<Tabs<(typeof userDb)[number]>
|
||||
tabs={userDb.map((user) => ({
|
||||
label: user.username,
|
||||
key: user.id.toString(),
|
||||
value: user,
|
||||
}))}
|
||||
onSelect={onSelect}
|
||||
/>
|
||||
<Tabs<(typeof userDb)[number]> tabs={userDbArray} onSelect={onSelect} />
|
||||
<div className={classes["tab-content"]}>
|
||||
<Typography typo={ETypo.TEXT_MD_REGULAR}>
|
||||
{selectedTab.id} - {selectedTab.username}
|
||||
|
Loading…
x
Reference in New Issue
Block a user