grid-combine
This commit is contained in:
parent
6dc931f233
commit
34d9fcfb74
@ -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;
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user