feat: improve task sorting and enhance UI component behavior

- Updated task sorting logic in TeamTaskReader to handle non-numeric IDs with stable lexicographic ordering.
- Refactored ActivityItem to remove unnecessary whitespace for cleaner code.
- Enhanced AttachmentPreviewItem to improve accessibility by adding pointer-events-none class for disabled state.
- Modified MessageComposer to ensure the summary remains compact during message sending.
- Added cleanup logic in useResizableColumns to handle global listeners and styles during drag events.
This commit is contained in:
iliya 2026-03-05 13:32:58 +02:00
parent 524f6f45d2
commit afb0173c05
5 changed files with 24 additions and 5 deletions

View file

@ -271,8 +271,16 @@ export class TeamTaskReader {
}
}
// Sort by numeric ID so kanban default order is deterministic (#1, #2, ..., #10, #11)
tasks.sort((a, b) => Number(a.id) - Number(b.id));
// Sort by numeric ID so kanban default order is deterministic (#1, #2, ..., #10, #11).
// Fall back to stable lexicographic ordering for unexpected non-numeric IDs.
tasks.sort((a, b) => {
const aIsNumeric = /^\d+$/.test(a.id);
const bIsNumeric = /^\d+$/.test(b.id);
if (aIsNumeric && bIsNumeric) return Number(a.id) - Number(b.id);
if (aIsNumeric) return -1;
if (bIsNumeric) return 1;
return a.id.localeCompare(b.id, undefined, { numeric: true, sensitivity: 'base' });
});
return tasks;
}

View file

@ -176,7 +176,6 @@ function linkifyMentionsInMarkdown(text: string, memberColorMap: Map<string, str
return `${prefix}[@${canonical}](mention://${encodeURIComponent(color)}/${encodeURIComponent(canonical)})`;
});
}
/** Render `#<digits>` in plain text as clickable inline elements with TaskTooltip. */
function linkifyTaskIds(text: string, onClick: (taskId: string) => void): React.ReactNode[] {
return text.split(/(#\d+)/g).map((part, i) => {

View file

@ -21,7 +21,7 @@ export const AttachmentPreviewItem = ({
return (
<div className="group/att relative flex shrink-0 items-center gap-2 rounded-md border border-[var(--color-border)] bg-[var(--color-surface)] p-1.5">
{disabled ? (
<div className="absolute inset-0 z-10 flex items-center justify-center rounded-md bg-black/50">
<div className="pointer-events-none absolute inset-0 z-10 flex items-center justify-center rounded-md bg-black/50">
<Ban size={18} className="text-red-400" />
</div>
) : null}

View file

@ -168,7 +168,8 @@ export const MessageComposer = ({
if (!canSend) return;
pendingSendRef.current = true;
const serialized = serializeChipsWithText(trimmed, chipDraft.chips);
onSend(recipient, serialized, serialized, attachments.length > 0 ? attachments : undefined);
// Summary should stay compact (no expanded chip markdown)
onSend(recipient, serialized, trimmed, attachments.length > 0 ? attachments : undefined);
}, [canSend, recipient, trimmed, onSend, attachments, chipDraft.chips]);
// Clear draft only after send completes successfully (sending: true → false, no error)

View file

@ -95,6 +95,17 @@ export function useResizableColumns({
});
}, [handlePointerMove, storageKey]);
// Safety: if the board unmounts or storageKey changes mid-drag, clean up global listeners/styles.
useEffect(() => {
return () => {
draggingRef.current = null;
document.removeEventListener('pointermove', handlePointerMove);
document.removeEventListener('pointerup', handlePointerUp);
document.body.style.cursor = '';
document.body.style.userSelect = '';
};
}, [handlePointerMove, handlePointerUp]);
const getHandleProps = useCallback(
(leftColumnId: string) => ({
onPointerDown: (e: React.PointerEvent) => {