fix(channels): tag-cell review follow-ups — rename-aware fit, measured gap, close reveal-all
Address the /code-review findings on the tag cell: - key the overflow-fit measurement on tag id+name so a RENAME (which changes chip width) re-runs the fit, not just add/remove; - reserve the '+N' pill space using the MEASURED gap (gap-1 is rem, scales with the font slider) instead of a px constant; - force the reveal-all hover popover's state closed when nothing is collapsed, so it can't linger open against an unmounted '+N' trigger.
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { useLayoutEffect, useRef, useState } from "react";
|
||||
import { useEffect, useLayoutEffect, useRef, useState } from "react";
|
||||
import { Check, Plus } from "lucide-react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { type Tag } from "../lib/api";
|
||||
@@ -146,7 +146,8 @@ export function ChannelTagsControl({
|
||||
const rowRef = useRef<HTMLDivElement | null>(null);
|
||||
const measureRef = useRef<HTMLDivElement | null>(null);
|
||||
const [visible, setVisible] = useState(attached.length);
|
||||
const idsKey = attached.map((tg) => tg.id).join(",");
|
||||
// Key on id AND name so a RENAME (which changes a chip's width) re-runs the fit, not just add/remove.
|
||||
const tagsKey = attached.map((tg) => `${tg.id}:${tg.name}`).join("|");
|
||||
|
||||
useLayoutEffect(() => {
|
||||
const row = rowRef.current;
|
||||
@@ -158,7 +159,12 @@ export function ChannelTagsControl({
|
||||
if (!pill) return;
|
||||
const pillW = pill.offsetWidth;
|
||||
const chips = nodes.slice(0, -1);
|
||||
const GAP = 4; // gap-1
|
||||
// Measure the real rendered gap (gap-1 is rem — it scales with the font slider) off the box
|
||||
// between the last chip and the sample pill, rather than assuming a px constant.
|
||||
const lastChip = chips[chips.length - 1];
|
||||
const gap = lastChip
|
||||
? Math.max(0, pill.offsetLeft - (lastChip.offsetLeft + lastChip.offsetWidth))
|
||||
: 0;
|
||||
const avail = row.clientWidth;
|
||||
let count = chips.length;
|
||||
for (let i = 0; i < chips.length; i++) {
|
||||
@@ -166,7 +172,7 @@ export function ChannelTagsControl({
|
||||
if (!chip) break;
|
||||
const right = chip.offsetLeft + chip.offsetWidth; // right edge within the measurer
|
||||
const moreAfter = i < chips.length - 1;
|
||||
if (right > avail - (moreAfter ? pillW + GAP : 0)) {
|
||||
if (right > avail - (moreAfter ? pillW + gap : 0)) {
|
||||
count = i;
|
||||
break;
|
||||
}
|
||||
@@ -178,7 +184,7 @@ export function ChannelTagsControl({
|
||||
const ro = new ResizeObserver(compute);
|
||||
ro.observe(row);
|
||||
return () => ro.disconnect();
|
||||
}, [idsKey]);
|
||||
}, [tagsKey]);
|
||||
|
||||
const hidden = attached.length - visible;
|
||||
|
||||
@@ -197,6 +203,11 @@ export function ChannelTagsControl({
|
||||
mode: "hover",
|
||||
placement: "bottom",
|
||||
});
|
||||
// If tags change so nothing is collapsed anymore, the "+N" trigger unmounts — force the reveal-all
|
||||
// popover's state closed so it can't linger open against a gone reference.
|
||||
useEffect(() => {
|
||||
if (hidden === 0 && overflowOpen) setOverflowOpen(false);
|
||||
}, [hidden, overflowOpen]);
|
||||
|
||||
const pillClass =
|
||||
"shrink-0 text-[10px] px-1.5 py-0.5 rounded-full bg-card text-muted border border-border";
|
||||
|
||||
Reference in New Issue
Block a user