폴더/뷰 전환 시 썸네일 미갱신 버그 수정
원인: JustifiedGrid 셀 본문에서 snapshot/id를 한 번만 캡처(const)했다. <For>는 인덱스 값이 같으면(뷰 전환 시 가시범위 [0..N] 동일) DOM 행을 재사용하므로, 재사용된 셀이 이전 폴더의 id·썸네일을 그대로 유지했다. (레이아웃 리사이즈 버그와 동일 계열) - id()/showImg()/isVideo()/version()을 snapshot() 기반 반응형 접근자로 변경 - 클릭/컨텍스트/드래그 핸들러도 호출 시점의 snapshot()을 읽도록 - img에 onLoad 추가 — 재사용된 img가 이전 onError로 display:none에 걸려 새 썸네일이 안 보이던 문제 방지 검증: 3개 하위폴더 픽스처에서 폴더 클릭 시 가시 썸네일 id가 folderA(1-40)/folderB(41-80)/folderC(83-140)로 즉시 전환 확인. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -370,47 +370,68 @@ export const JustifiedGrid: Component = () => {
|
|||||||
</Show>
|
</Show>
|
||||||
<For each={indices()}>
|
<For each={indices()}>
|
||||||
{(i) => {
|
{(i) => {
|
||||||
const snap = snapshot()!;
|
// 모든 파생값은 반응형으로 — For가 같은 인덱스 값에 대해 DOM을 재사용해도
|
||||||
const id = snap.id(i);
|
// 뷰(폴더) 전환 시 스냅샷이 바뀌면 id/썸네일/선택상태가 즉시 갱신되도록 한다.
|
||||||
// 위치/크기는 반응형으로 — layout() 변경(리사이즈/줌) 시 For가 DOM을
|
|
||||||
// 재사용해도 style이 갱신되도록 한다.
|
|
||||||
const box = () => {
|
const box = () => {
|
||||||
const l = layout();
|
const l = layout();
|
||||||
return l
|
return l
|
||||||
? [l.boxes[i * 4], l.boxes[i * 4 + 1], l.boxes[i * 4 + 2], l.boxes[i * 4 + 3]]
|
? [l.boxes[i * 4], l.boxes[i * 4 + 1], l.boxes[i * 4 + 2], l.boxes[i * 4 + 3]]
|
||||||
: [0, 0, 0, 0];
|
: [0, 0, 0, 0];
|
||||||
};
|
};
|
||||||
const version = () => thumbVersions[id] ?? 0;
|
const id = () => {
|
||||||
const showImg = () => snap.hasThumb(i) || version() > 0;
|
const s = snapshot();
|
||||||
|
return s ? s.id(i) : -1;
|
||||||
|
};
|
||||||
|
const version = () => thumbVersions[id()] ?? 0;
|
||||||
|
const showImg = () => {
|
||||||
|
const s = snapshot();
|
||||||
|
return !!s && (s.hasThumb(i) || version() > 0);
|
||||||
|
};
|
||||||
|
const isVideo = () => {
|
||||||
|
const s = snapshot();
|
||||||
|
return !!s && s.kind(i) === 1;
|
||||||
|
};
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
class="absolute cursor-pointer overflow-hidden rounded-[3px] bg-[var(--grid-cell-bg)] outline-offset-[-2px]"
|
class="absolute cursor-pointer overflow-hidden rounded-[3px] bg-[var(--grid-cell-bg)] outline-offset-[-2px]"
|
||||||
classList={{
|
classList={{
|
||||||
"outline outline-2 outline-[var(--accent)]": selected().has(id),
|
"outline outline-2 outline-[var(--accent)]": selected().has(id()),
|
||||||
}}
|
}}
|
||||||
style={{
|
style={{
|
||||||
transform: `translate(${box()[0]}px, ${box()[1]}px)`,
|
transform: `translate(${box()[0]}px, ${box()[1]}px)`,
|
||||||
width: `${box()[2]}px`,
|
width: `${box()[2]}px`,
|
||||||
height: `${box()[3]}px`,
|
height: `${box()[3]}px`,
|
||||||
}}
|
}}
|
||||||
onClick={(e) => onCellClick(e, snap, i)}
|
onClick={(e) => {
|
||||||
|
const s = snapshot();
|
||||||
|
if (s) onCellClick(e, s, i);
|
||||||
|
}}
|
||||||
onDblClick={() => openLightbox(i)}
|
onDblClick={() => openLightbox(i)}
|
||||||
onContextMenu={(e) => onCellContextMenu(e, snap, i)}
|
onContextMenu={(e) => {
|
||||||
|
const s = snapshot();
|
||||||
|
if (s) onCellContextMenu(e, s, i);
|
||||||
|
}}
|
||||||
draggable={true}
|
draggable={true}
|
||||||
onDragStart={(e) => onCellDragStart(e, snap, i)}
|
onDragStart={(e) => {
|
||||||
|
const s = snapshot();
|
||||||
|
if (s) onCellDragStart(e, s, i);
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<Show when={showImg() && info()}>
|
<Show when={showImg() && info()}>
|
||||||
<img
|
<img
|
||||||
src={`${thumbUrl(info()!, id)}&v=${version()}`}
|
src={`${thumbUrl(info()!, id())}&v=${version()}`}
|
||||||
class="h-full w-full object-cover"
|
class="h-full w-full object-cover"
|
||||||
decoding="async"
|
decoding="async"
|
||||||
draggable={false}
|
draggable={false}
|
||||||
|
onLoad={(e) => {
|
||||||
|
(e.currentTarget as HTMLImageElement).style.display = "";
|
||||||
|
}}
|
||||||
onError={(e) => {
|
onError={(e) => {
|
||||||
(e.currentTarget as HTMLImageElement).style.display = "none";
|
(e.currentTarget as HTMLImageElement).style.display = "none";
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Show>
|
</Show>
|
||||||
<Show when={snap.kind(i) === 1}>
|
<Show when={isVideo()}>
|
||||||
<span class="absolute bottom-1 right-1 rounded bg-black/60 px-1 text-[10px] text-white">
|
<span class="absolute bottom-1 right-1 rounded bg-black/60 px-1 text-[10px] text-white">
|
||||||
영상
|
영상
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
Reference in New Issue
Block a user