| 841 | } |
| 842 | |
| 843 | function CalendarTaskRow({ |
| 844 | task, |
| 845 | isOverdue, |
| 846 | isActive, |
| 847 | buttonRef, |
| 848 | onToggle, |
| 849 | onOpen, |
| 850 | onContextMenu, |
| 851 | onPointerDownTask |
| 852 | }: RowProps): JSX.Element { |
| 853 | return ( |
| 854 | // Pointer-based drag (see TasksCalendar) instead of native HTML5 DnD. |
| 855 | // role/tabIndex/onKeyDown keep it keyboard-accessible. |
| 856 | <div |
| 857 | role="button" |
| 858 | tabIndex={0} |
| 859 | ref={buttonRef ?? undefined} |
| 860 | onClick={onOpen} |
| 861 | onKeyDown={(e) => { |
| 862 | if (e.key === 'Enter' || e.key === ' ') { |
| 863 | e.preventDefault() |
| 864 | onOpen() |
| 865 | } |
| 866 | }} |
| 867 | onPointerDown={onPointerDownTask} |
| 868 | onContextMenu={onContextMenu} |
| 869 | title="Drag to a day to reschedule · right-click for actions" |
| 870 | className={[ |
| 871 | 'flex w-full cursor-grab select-none items-center gap-2 rounded-md border-l-2 px-2 py-1 text-left text-sm active:cursor-grabbing', |
| 872 | isOverdue ? 'border-rose-500/70' : 'border-transparent', |
| 873 | isActive ? 'bg-accent/10 ring-1 ring-inset ring-accent/40' : 'hover:bg-paper-200/60' |
| 874 | ].join(' ')} |
| 875 | > |
| 876 | <span |
| 877 | role="checkbox" |
| 878 | aria-checked={task.checked} |
| 879 | onClick={(e) => { |
| 880 | e.stopPropagation() |
| 881 | onToggle() |
| 882 | }} |
| 883 | className={[ |
| 884 | 'flex h-4 w-4 shrink-0 cursor-pointer items-center justify-center rounded transition-colors', |
| 885 | task.checked |
| 886 | ? 'border border-accent bg-accent text-white' |
| 887 | : 'border border-paper-400/70 hover:bg-paper-200/80' |
| 888 | ].join(' ')} |
| 889 | > |
| 890 | {task.checked && ( |
| 891 | <svg |
| 892 | viewBox="0 0 24 24" |
| 893 | width="11" |
| 894 | height="11" |
| 895 | fill="none" |
| 896 | stroke="currentColor" |
| 897 | strokeWidth="3.5" |
| 898 | strokeLinecap="round" |
| 899 | strokeLinejoin="round" |
| 900 | > |