mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-08-24 03:55:49 -05:00
Lock tier heights when dragging to prevent infinite loop
The bug is that when a tier is about to wrap
(increase in height), especially on mobile it will loop the drop preview
for the lower and upper tier till the page crashes.
Example crash page (user report): /tier-list-maker?type=stage-mode&canAddDuplicates=false&state=('Vs!%5BHxJXj4655_sJSj8c42_aJAjd23f_bJBGbfe84d_cJCG5dbb63_wJDG20b2aa_zJFG4169e1W~VItems!%5B%5B'V-zK0O1-SZEwK1k3O5O4q4k5u5PEcK1q1u4u4O3q6O5q6QEbK0k2u2QEaK2O3k6PEsK0u2k6UExK0q3Uv%5D)*('typehstage-mode'~idh.')%2CEvY'V-G'~colorh%23H('idhV-J'~namehK'Y*L1775494063N.*O-SZNP-TCQ-CBU-RMVtierW')%5DY%2C%5B_.Hh!'jGffkPNqQNuUNvW%5DwL330zL553%01zwvuqkjh_YWVUQPONLKJHGE.*_
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
.item {
|
||||
min-height: 50px;
|
||||
cursor: move;
|
||||
touch-action: none;
|
||||
user-select: none;
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
} from "@dnd-kit/sortable";
|
||||
import clsx from "clsx";
|
||||
import { ChevronDown, ChevronUp, Trash } from "lucide-react";
|
||||
import { useLayoutEffect, useRef } from "react";
|
||||
import { Button } from "react-aria-components";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { SendouButton } from "~/components/elements/Button";
|
||||
@@ -28,6 +29,7 @@ interface TierRowProps {
|
||||
export function TierRow({ tier }: TierRowProps) {
|
||||
const {
|
||||
state,
|
||||
activeItem,
|
||||
getItemsInTier,
|
||||
handleRemoveTier,
|
||||
handleRenameTier,
|
||||
@@ -44,6 +46,11 @@ export function TierRow({ tier }: TierRowProps) {
|
||||
id: tier.id,
|
||||
});
|
||||
|
||||
const combinedRef = useLockedHeightWhileDragging({
|
||||
setNodeRef,
|
||||
isDragging: activeItem !== null,
|
||||
});
|
||||
|
||||
const tierIndex = state.tiers.findIndex((t) => t.id === tier.id);
|
||||
const isFirstTier = tierIndex === 0;
|
||||
const isLastTier = tierIndex === state.tiers.length - 1;
|
||||
@@ -121,7 +128,7 @@ export function TierRow({ tier }: TierRowProps) {
|
||||
) : null}
|
||||
|
||||
<div
|
||||
ref={setNodeRef}
|
||||
ref={combinedRef}
|
||||
style={{
|
||||
borderRadius: screenshotMode ? "var(--radius-field)" : undefined,
|
||||
}}
|
||||
@@ -171,6 +178,49 @@ export function TierRow({ tier }: TierRowProps) {
|
||||
);
|
||||
}
|
||||
|
||||
function useLockedHeightWhileDragging({
|
||||
setNodeRef,
|
||||
isDragging,
|
||||
}: {
|
||||
setNodeRef: (node: HTMLElement | null) => void;
|
||||
isDragging: boolean;
|
||||
}) {
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
|
||||
const combinedRef = (node: HTMLDivElement | null) => {
|
||||
ref.current = node;
|
||||
setNodeRef(node);
|
||||
};
|
||||
|
||||
useLayoutEffect(() => {
|
||||
const el = ref.current;
|
||||
if (!el) return;
|
||||
|
||||
if (isDragging) {
|
||||
const rect = el.getBoundingClientRect();
|
||||
const firstItem = el.firstElementChild;
|
||||
const topOffset = firstItem
|
||||
? firstItem.getBoundingClientRect().top - rect.top
|
||||
: undefined;
|
||||
|
||||
el.style.height = `${rect.height}px`;
|
||||
el.style.overflow = "hidden";
|
||||
|
||||
if (topOffset !== undefined) {
|
||||
el.style.alignContent = "flex-start";
|
||||
el.style.paddingTop = `${topOffset}px`;
|
||||
}
|
||||
} else {
|
||||
el.style.height = "";
|
||||
el.style.overflow = "";
|
||||
el.style.alignContent = "";
|
||||
el.style.paddingTop = "";
|
||||
}
|
||||
}, [isDragging]);
|
||||
|
||||
return combinedRef;
|
||||
}
|
||||
|
||||
function tierNameFontSize(name: string) {
|
||||
const length = name.length;
|
||||
for (const breakpoint of TIER_NAME_FONT_SIZE_BREAKPOINTS) {
|
||||
|
||||
Reference in New Issue
Block a user