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:
parent
524f6f45d2
commit
afb0173c05
5 changed files with 24 additions and 5 deletions
|
|
@ -271,8 +271,16 @@ export class TeamTaskReader {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sort by numeric ID so kanban default order is deterministic (#1, #2, ..., #10, #11)
|
// Sort by numeric ID so kanban default order is deterministic (#1, #2, ..., #10, #11).
|
||||||
tasks.sort((a, b) => Number(a.id) - Number(b.id));
|
// 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;
|
return tasks;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -176,7 +176,6 @@ function linkifyMentionsInMarkdown(text: string, memberColorMap: Map<string, str
|
||||||
return `${prefix}[@${canonical}](mention://${encodeURIComponent(color)}/${encodeURIComponent(canonical)})`;
|
return `${prefix}[@${canonical}](mention://${encodeURIComponent(color)}/${encodeURIComponent(canonical)})`;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Render `#<digits>` in plain text as clickable inline elements with TaskTooltip. */
|
/** Render `#<digits>` in plain text as clickable inline elements with TaskTooltip. */
|
||||||
function linkifyTaskIds(text: string, onClick: (taskId: string) => void): React.ReactNode[] {
|
function linkifyTaskIds(text: string, onClick: (taskId: string) => void): React.ReactNode[] {
|
||||||
return text.split(/(#\d+)/g).map((part, i) => {
|
return text.split(/(#\d+)/g).map((part, i) => {
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ export const AttachmentPreviewItem = ({
|
||||||
return (
|
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">
|
<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 ? (
|
{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" />
|
<Ban size={18} className="text-red-400" />
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
|
||||||
|
|
@ -168,7 +168,8 @@ export const MessageComposer = ({
|
||||||
if (!canSend) return;
|
if (!canSend) return;
|
||||||
pendingSendRef.current = true;
|
pendingSendRef.current = true;
|
||||||
const serialized = serializeChipsWithText(trimmed, chipDraft.chips);
|
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]);
|
}, [canSend, recipient, trimmed, onSend, attachments, chipDraft.chips]);
|
||||||
|
|
||||||
// Clear draft only after send completes successfully (sending: true → false, no error)
|
// Clear draft only after send completes successfully (sending: true → false, no error)
|
||||||
|
|
|
||||||
|
|
@ -95,6 +95,17 @@ export function useResizableColumns({
|
||||||
});
|
});
|
||||||
}, [handlePointerMove, storageKey]);
|
}, [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(
|
const getHandleProps = useCallback(
|
||||||
(leftColumnId: string) => ({
|
(leftColumnId: string) => ({
|
||||||
onPointerDown: (e: React.PointerEvent) => {
|
onPointerDown: (e: React.PointerEvent) => {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue