import { memo, type PointerEvent } from "react"; import { Link } from "react-router"; import { BOARD_CARD_STATE } from "../../issues/modal.ts"; import { CW, JIRA_URL, KIND, PRIO } from "../constants.ts"; import { lineClamp, type Rect, type RelBox } from "../layout/layout.ts"; import { fmtDate, shortName, unassigned } from "../model/util.ts"; import type { VisNode } from "../types.ts"; export interface CardProps { node: VisNode; box?: Rect; // нет — карточка слоя измерения (ширина CW, высота по содержимому) rel?: RelBox; multiSprint: boolean; sprintSel: Set; onPointerDown?: (ev: PointerEvent, key: string) => void; onAutoSize?: (key: string) => void; } // T11.1: при перетаскивании одной карточки place() создаёт новые box у всех — сравниваем box по значению export function cardPropsEqual(a: CardProps, b: CardProps): boolean { const sameBox = a.box === b.box || (!!a.box && !!b.box && a.box.x === b.box.x && a.box.y === b.box.y && a.box.w === b.box.w && a.box.h === b.box.h); return sameBox && a.node === b.node && a.rel === b.rel && a.multiSprint === b.multiSprint && a.sprintSel === b.sprintSel && a.onPointerDown === b.onPointerDown && a.onAutoSize === b.onAutoSize; } export const Card = memo(function Card({ node: n, box, rel, multiSprint, sprintSel, onPointerDown, onAutoSize }: CardProps) { const cls = "card" + (n.dim || n.stub ? " out" : "") + (n.crit ? " crit" : "") + (n.focus ? " focus" : ""); const style = box ? { left: box.x, top: box.y, width: box.w, height: box.h } : { width: CW }; const showTag = !n.stub && (multiSprint || n.dim); // метка спринта — если спринтов на доске больше одного или это сосед return (
onPointerDown(e, n.key) : undefined}>
{n.key} {!n.stub && ⓘ} {showTag && (n.sprint ? {n.sprint.replace(/^AWPCA_/, "")} : без спринта)}
{n.title}
{n.kind} {n.prio}
{(n.status || n.due) && (
{n.status && {n.status}} {n.due && до {fmtDate(n.due)}}
)} {!n.stub &&
{shortName(n.who)}
}
onAutoSize(n.key) : undefined} />
); }, cardPropsEqual);