diff --git a/site/components/InteractiveGraphics/InteractiveGraphics.tsx b/site/components/InteractiveGraphics/InteractiveGraphics.tsx index de014e1..fc14747 100644 --- a/site/components/InteractiveGraphics/InteractiveGraphics.tsx +++ b/site/components/InteractiveGraphics/InteractiveGraphics.tsx @@ -54,6 +54,46 @@ export type GraphicsObjectClickEvent = { object: any } +const normalizeObjectLimit = (objectLimit?: number) => { + if (objectLimit === undefined || !Number.isFinite(objectLimit)) return null + const normalizedLimit = Math.floor(objectLimit) + return normalizedLimit > 0 ? normalizedLimit : null +} + +export const limitObjectGroups = >( + groups: T, + objectLimit?: number, +): { + groups: T + totalObjectCount: number + isLimitReached: boolean +} => { + const totalObjectCount = Object.values(groups).reduce( + (count, group) => count + group.length, + 0, + ) + const normalizedLimit = normalizeObjectLimit(objectLimit) + + if (normalizedLimit === null) { + return { groups, totalObjectCount, isLimitReached: false } + } + + let remainingObjects = normalizedLimit + const limitedGroups = Object.fromEntries( + Object.entries(groups).map(([groupName, group]) => { + const limitedGroup = group.slice(0, remainingObjects) + remainingObjects = Math.max(remainingObjects - limitedGroup.length, 0) + return [groupName, limitedGroup] + }), + ) as unknown as T + + return { + groups: limitedGroups, + totalObjectCount, + isLimitReached: totalObjectCount > normalizedLimit, + } +} + export const InteractiveGraphics = ({ graphics, onObjectClicked, @@ -421,65 +461,96 @@ export const InteractiveGraphics = ({ filterLayerAndStep, }) - const filterAndLimit = ( + const filterObjects = ( objects: T[] | undefined, filterFn: (obj: T) => boolean, ): (T & { originalIndex: number })[] => { if (!objects) return [] - const filtered = objects + return objects .map((obj, index) => ({ ...obj, originalIndex: index })) .filter(filterFn) - return objectLimit ? filtered.slice(-objectLimit) : filtered } const filteredLines = useMemo( () => - filterAndLimit(graphics.lines, filterLines).sort( + filterObjects(graphics.lines, filterLines).sort( (a, b) => (a.zIndex ?? 0) - (b.zIndex ?? 0) || a.originalIndex - b.originalIndex, ), - [graphics.lines, filterLines, objectLimit], + [graphics.lines, filterLines], ) const filteredInfiniteLines = useMemo( - () => filterAndLimit(graphics.infiniteLines, filterLayerAndStep), - [graphics.infiniteLines, filterLayerAndStep, objectLimit], + () => filterObjects(graphics.infiniteLines, filterLayerAndStep), + [graphics.infiniteLines, filterLayerAndStep], ) const filteredRects = useMemo( - () => sortRectsByArea(filterAndLimit(graphics.rects, filterRects)), - [graphics.rects, filterRects, objectLimit], + () => sortRectsByArea(filterObjects(graphics.rects, filterRects)), + [graphics.rects, filterRects], ) const filteredPolygons = useMemo( - () => filterAndLimit(graphics.polygons, filterPolygons), - [graphics.polygons, filterPolygons, objectLimit], + () => filterObjects(graphics.polygons, filterPolygons), + [graphics.polygons, filterPolygons], ) const filteredPoints = useMemo( - () => filterAndLimit(graphics.points, filterPoints), - [graphics.points, filterPoints, objectLimit], + () => filterObjects(graphics.points, filterPoints), + [graphics.points, filterPoints], ) const filteredCircles = useMemo( - () => filterAndLimit(graphics.circles, filterCircles), - [graphics.circles, filterCircles, objectLimit], + () => filterObjects(graphics.circles, filterCircles), + [graphics.circles, filterCircles], ) const filteredTexts = useMemo( - () => filterAndLimit(graphics.texts, filterTexts), - [graphics.texts, filterTexts, objectLimit], + () => filterObjects(graphics.texts, filterTexts), + [graphics.texts, filterTexts], ) const filteredArrows = useMemo( - () => filterAndLimit(graphics.arrows, filterArrows), - [graphics.arrows, filterArrows, objectLimit], + () => filterObjects(graphics.arrows, filterArrows), + [graphics.arrows, filterArrows], ) - const totalFilteredObjects = - filteredInfiniteLines.length + - filteredLines.length + - filteredRects.length + - filteredPolygons.length + - filteredPoints.length + - filteredCircles.length + - filteredTexts.length + - filteredArrows.length - const isLimitReached = objectLimit && totalFilteredObjects > objectLimit + const { + groups: limitedObjectGroups, + totalObjectCount, + isLimitReached, + } = useMemo( + () => + limitObjectGroups( + { + filteredArrows, + filteredInfiniteLines, + filteredLines, + filteredRects, + filteredPolygons, + filteredCircles, + filteredTexts, + filteredPoints, + }, + objectLimit, + ), + [ + filteredArrows, + filteredInfiniteLines, + filteredLines, + filteredRects, + filteredPolygons, + filteredCircles, + filteredTexts, + filteredPoints, + objectLimit, + ], + ) + + const { + filteredArrows: visibleArrows, + filteredInfiniteLines: visibleInfiniteLines, + filteredLines: visibleLines, + filteredRects: visibleRects, + filteredPolygons: visiblePolygons, + filteredCircles: visibleCircles, + filteredTexts: visibleTexts, + filteredPoints: visiblePoints, + } = limitedObjectGroups return (
@@ -552,15 +623,16 @@ export const InteractiveGraphics = ({ /> Show last step - {isLimitReached && ( - - Display limited to {objectLimit} objects. Received:{" "} - {totalFilteredObjects}. - - )}
)} + {isLimitReached && ( + + Display limited to {normalizeObjectLimit(objectLimit)} objects. + Received: {totalObjectCount}. + + )} +