grid-combine

This commit is contained in:
lukylix 2023-07-10 15:11:59 +02:00
parent 6dc931f233
commit 34d9fcfb74
2 changed files with 145 additions and 85 deletions

View File

@ -1,7 +1,7 @@
import { memo, useCallback, useEffect, useMemo, useRef, useState } from "react";
import classNames from "classnames"; import classNames from "classnames";
import Item from "components/services/item"; import Item from "components/services/item";
import { useEffect, useRef, useState } from "react";
const columnMap = [ const columnMap = [
"grid-cols-1 md:grid-cols-1 lg:grid-cols-1", "grid-cols-1 md:grid-cols-1 lg:grid-cols-1",
@ -29,107 +29,167 @@ const subcolumnMap = [
const itemRemSizeMap = [1, 1, 9, 8, 7, 7, 7, 7, 7]; const itemRemSizeMap = [1, 1, 9, 8, 7, 7, 7, 7, 7];
export default function List({ group, services, layout, isGroup = false }) { function areArraysEqual(array1, array2) {
const containerRef = useRef(null); if (array1.length === 0 && array2.length === 0) return true;
const [childrensToSlice, setChildrensToSlice] = useState(0);
const numberOfServices = services.filter((e) => e).length; if (array1.length !== array2.length) {
const servicesTopRows = services.filter((v) => v).slice(0, numberOfServices - childrensToSlice); return false;
const servicesBottomRows = services.filter((v) => v).slice(numberOfServices - childrensToSlice); }
if (servicesBottomRows.length > 0) console.log(servicesBottomRows); const sortedArray1 = JSON.stringify(array1.slice().sort());
const sortedArray2 = JSON.stringify(array2.slice().sort());
let gridClassName = isGroup ? subcolumnMap[layout.columns] : columnMap[layout?.columns]; return sortedArray1 === sortedArray2;
let gridClassNameBottom = isGroup }
? subcolumnMap[servicesBottomRows?.length]
: servicesBottomRows[servicesTopRows?.length];
if (gridClassName) gridClassName = ` grid auto-rows-max ${gridClassName} gap-x-2`;
if (gridClassNameBottom) gridClassNameBottom = ` grid auto-rows-max ${gridClassNameBottom} gap-x-2`;
if (!gridClassName) gridClassName = " flex flex-wrap gap-x-2";
if (!gridClassNameBottom) gridClassNameBottom = " flex flex-wrap gap-x-2";
useEffect(() => { // eslint-disable-next-line react/display-name
const resizeObserver = new ResizeObserver((entries) => { const List = memo(
for (let i = 0; i < entries.length; i += 1) { ({ group, services, layout, isGroup = false, propagate = [], setPropagate, isStyleCombined = false }) => {
const entry = entries[i]; const containerRef = useRef(null);
const { width } = entry.contentRect; const [childrensToSlice, setChildrensToSlice] = useState(0);
const remSize = parseFloat(getComputedStyle(document.documentElement).fontSize); const [servicesToPropagate, setServicesToPropagate] = useState({});
const itemRemSize = itemRemSizeMap[parseInt(layout?.columns, 10)]; const setPropagateWrapper = (id) => (value) => {
if (areArraysEqual(servicesToPropagate[id] || [], value)) return;
const remWidth = width / remSize; setServicesToPropagate((prev) => ({ ...prev, [id]: value }));
const maxChildrenFit = Math.floor(remWidth / itemRemSize);
if (numberOfServices < maxChildrenFit) return setChildrensToSlice(0);
const toSlice =
remWidth / itemRemSize > 1 && numberOfServices > Math.min(maxChildrenFit, layout.columns)
? numberOfServices % Math.min(maxChildrenFit, layout.columns)
: 0;
setChildrensToSlice(toSlice);
}
return true;
});
if (containerRef.current) {
resizeObserver.observe(containerRef.current);
}
return () => {
resizeObserver.disconnect();
}; };
}, [numberOfServices]); const numberOfServices = useMemo(() => [...propagate, ...services].filter((e) => e).length, [propagate, services]);
const servicesTopRows = useMemo(
() => [...propagate, ...services].filter((v) => v).slice(0, numberOfServices - childrensToSlice),
[propagate, services, childrensToSlice, numberOfServices]
);
const servicesBottomRows = useMemo(
() => [...propagate, ...services].filter((v) => v).slice(numberOfServices - childrensToSlice),
[propagate, services, childrensToSlice, numberOfServices]
);
return ( const setPropagateCallback = useCallback(setPropagate, [setPropagate]);
<ul
className={classNames( useEffect(() => {
layout?.style === "row" ? gridClassName : "flex flex-col", if (typeof setPropagateCallback !== "function") return;
isGroup ? undefined : "mt-3", setPropagateCallback(servicesBottomRows.length > 0 ? servicesBottomRows : []);
"@container" }, [servicesBottomRows, setPropagateCallback]);
)}
ref={containerRef} const isStyleCombinedValue = useMemo(
> () => layout?.style === undefined || layout?.style === "grid-combine" || isStyleCombined,
{servicesTopRows.length > 0 && [layout?.style, isStyleCombined]
servicesTopRows.map((service) => );
service.type === "grouped-service" ? (
<List const servicesTopAfterPropagate = useMemo(() => {
key={service.name} const groupedServiceIndexReverse = [...servicesTopRows].reverse().findIndex((e) => e.type === "grouped-service");
group={service.name} const groupedServiceIndex = servicesTopRows.length - groupedServiceIndexReverse;
services={service.services} const insertIndex = groupedServiceIndex || servicesTopRows.length;
layout={{ const servicesToAdd = servicesToPropagate[groupedServiceIndex] || [];
columns: parseInt(service.name, 10) || 1, return [
style: "row", ...servicesTopRows.slice(0, insertIndex),
}} ...servicesToAdd,
isGroup ...servicesTopRows.slice(insertIndex, servicesTopRows.length),
/> ];
) : ( }, [servicesTopRows, servicesToPropagate]);
<Item key={service.container ?? service.app ?? service.name} service={service} group={group} />
) const [gridClassNameTop, gridClassNameBottom] = useMemo(() => {
let gridClassName = isGroup ? subcolumnMap[layout.columns] : columnMap[layout?.columns];
let gridClassNameB = isGroup
? subcolumnMap[servicesBottomRows?.length]
: servicesBottomRows[servicesTopRows?.length];
if (gridClassName) gridClassName = ` grid auto-rows-max ${gridClassName} gap-x-2`;
if (gridClassNameB) gridClassNameB = ` grid auto-rows-max ${gridClassNameB} gap-x-2`;
if (!gridClassName) gridClassName = " flex flex-wrap gap-x-2";
if (!gridClassNameB) gridClassNameB = " flex flex-wrap gap-x-2";
return [gridClassName, gridClassNameB];
}, [layout?.columns, servicesBottomRows, servicesTopRows, isGroup]);
useEffect(() => {
const resizeObserver = new ResizeObserver((entries) => {
for (let i = 0; i < entries.length; i += 1) {
const entry = entries[i];
const { width } = entry.contentRect;
const remSize = parseFloat(getComputedStyle(document.documentElement).fontSize);
const itemRemSize = itemRemSizeMap[parseInt(layout?.columns || 1, 10)];
const remWidth = width / remSize;
const maxChildrenFit = Math.floor(remWidth / itemRemSize);
if (!itemRemSize || !maxChildrenFit || numberOfServices < maxChildrenFit || !(remWidth / itemRemSize > 1))
return setChildrensToSlice(0);
const toSlice = numberOfServices % Math.min(maxChildrenFit, layout.columns || 1);
setChildrensToSlice(toSlice);
}
return true;
});
if (containerRef.current) {
resizeObserver.observe(containerRef.current);
}
return () => {
resizeObserver.disconnect();
};
}, [numberOfServices, layout?.columns]);
return (
<ul
className={classNames(
layout?.style === "row" ? gridClassNameTop : "flex flex-col",
isGroup ? undefined : "mt-3",
"@container"
)} )}
{servicesBottomRows.length > 0 && ( ref={containerRef}
<ul >
className={classNames( {servicesTopAfterPropagate.length > 0 &&
layout?.style === "row" ? gridClassNameBottom : "flex flex-col", servicesTopAfterPropagate.map((service, i) =>
isGroup ? undefined : "mt-3",
"col-span-full"
)}
>
{servicesBottomRows.map((service) =>
service.type === "grouped-service" ? ( service.type === "grouped-service" ? (
<List <List
key={service.name} key={service.name}
group={service.name} group={service.name}
services={service.services} services={service.services}
layout={{ layout={{
columns: service.services?.length, ...layout,
columns: parseInt(service.name, 10) || 1,
style: "row", style: "row",
}} }}
setPropagate={setPropagate || setPropagateWrapper(i + 1)}
propagate={(isStyleCombinedValue && servicesToPropagate[i]) || []}
isGroup isGroup
isStyleCombined={isStyleCombinedValue}
/> />
) : ( ) : (
<Item key={service.container ?? service.app ?? service.name} service={service} group={group} /> <Item key={service.container ?? service.app ?? service.name} service={service} group={group} />
) )
)} )}
</ul> {servicesBottomRows.length > 0 && !isStyleCombined && (
)} <ul
</ul> className={classNames(
); layout?.style === "row" ? gridClassNameBottom : "flex flex-col",
} isGroup ? undefined : "mt-3",
"col-span-full"
)}
>
{servicesBottomRows.map((service, i) =>
service.type === "grouped-service" ? (
<List
key={service.name}
group={service.name}
services={service.services}
layout={{
columns: service.services?.length,
style: "row",
}}
setPropagate={setPropagate || setPropagateWrapper(i + 1)}
propagate={servicesToPropagate[i] || []}
isGroup
isStyleCombined={isStyleCombinedValue}
isDeep={isGroup}
/>
) : (
<Item key={service.container ?? service.app ?? service.name} service={service} group={group} />
)
)}
{}
</ul>
)}
</ul>
);
}
);
export default List;

View File

@ -59,7 +59,7 @@ export default function Container({ error = false, children, service }) {
if (numberOfChildren > 1 && remWidth > 12) { if (numberOfChildren > 1 && remWidth > 12) {
if (numberOfChildren <= maxChildrenFit) return setChildrensToSlice(0); if (numberOfChildren <= maxChildrenFit) return setChildrensToSlice(0);
setChildrensToSlice(remWidth / 6 > 1 ? parseInt(numberOfChildren / maxChildrenFit, 10) : 0); setChildrensToSlice(remWidth / 6 > 1 ? numberOfChildren % maxChildrenFit : 0);
} }
} }
return true; return true;