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