-
Notifications
You must be signed in to change notification settings - Fork 204
Expand file tree
/
Copy pathCreditLineRowMenu.tsx
More file actions
149 lines (139 loc) · 4.87 KB
/
Copy pathCreditLineRowMenu.tsx
File metadata and controls
149 lines (139 loc) · 4.87 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
import React, { useState, useRef, useEffect } from 'react';
import { MoreHorizontal, ArrowUpCircle, ArrowDownCircle, Calendar, Info, Snowflake } from 'lucide-react';
import { Link } from 'react-router-dom';
interface CreditLineRowMenuProps {
lineId: string;
lineName: string;
frozen?: boolean;
onRepay?: () => void;
onSchedule?: (lineId: string) => void;
onDetails?: (lineId: string) => void;
onFreeze?: (lineId: string) => void;
onUnfreeze?: (lineId: string) => void;
}
/**
* A quick-action dropdown menu for credit line rows.
*
* Provides actions: Repay, Draw, Schedule, and Details.
*
* Accessibility:
* - Uses aria-haspopup and aria-expanded to indicate menu state.
* - Includes keyboard support (Escape to close).
* - Handles clicking outside to close.
* - Uses appropriate roles (menu, menuitem).
*/
export function CreditLineRowMenu({
lineId,
lineName,
frozen,
onRepay,
onSchedule,
onDetails,
onFreeze,
onUnfreeze,
}: CreditLineRowMenuProps) {
const [isOpen, setIsOpen] = useState(false);
const menuRef = useRef<HTMLDivElement>(null);
// Close menu when clicking outside
useEffect(() => {
function handleClickOutside(event: MouseEvent) {
if (menuRef.current && !menuRef.current.contains(event.target as Node)) {
setIsOpen(false);
}
}
document.addEventListener("mousedown", handleClickOutside);
return () => document.removeEventListener("mousedown", handleClickOutside);
}, []);
// Close on Escape key
useEffect(() => {
function handleKeyDown(event: KeyboardEvent) {
if (event.key === 'Escape') {
setIsOpen(false);
}
}
document.addEventListener("keydown", handleKeyDown);
return () => document.removeEventListener("keydown", handleKeyDown);
}, []);
return (
<div className="relative" ref={menuRef}>
<button
onClick={() => setIsOpen(!isOpen)}
className="p-2 text-muted hover:text-foreground hover:bg-surface rounded-full transition-colors focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-accent"
aria-haspopup="true"
aria-expanded={isOpen}
aria-label={`Menu for ${lineName}`}
>
<MoreHorizontal className="w-5 h-5" />
</button>
{isOpen && (
<div
className="absolute right-0 mt-2 w-48 origin-top-right rounded-lg border border-border bg-surface py-1 shadow-xl ring-1 ring-black ring-opacity-5 focus:outline-none z-50"
role="menu"
aria-orientation="vertical"
>
{/* Repay */}
<button
onClick={() => {
setIsOpen(false);
if (onRepay) onRepay();
}}
className="flex w-full items-center gap-3 px-4 py-2 text-sm text-foreground hover:bg-accent/10 hover:text-accent transition-colors text-left"
role="menuitem"
>
<ArrowUpCircle className="w-4 h-4" />
Repay
</button>
{/* Draw */}
<Link
to={`/draw-credit?line=${lineId}`}
onClick={() => setIsOpen(false)}
className="flex w-full items-center gap-3 px-4 py-2 text-sm text-foreground hover:bg-accent/10 hover:text-accent transition-colors"
role="menuitem"
>
<ArrowDownCircle className="w-4 h-4" />
Draw
</Link>
{/* Freeze / Unfreeze */}
{(onFreeze || onUnfreeze) && (
<button
onClick={() => {
setIsOpen(false);
if (frozen && onUnfreeze) onUnfreeze(lineId);
else if (!frozen && onFreeze) onFreeze(lineId);
}}
className="flex w-full items-center gap-3 px-4 py-2 text-sm text-foreground hover:bg-accent/10 hover:text-accent transition-colors text-left"
role="menuitem"
>
<Snowflake className="w-4 h-4" />
{frozen ? 'Unfreeze' : 'Freeze'}
</button>
)}
{/* Schedule */}
<button
onClick={() => {
setIsOpen(false);
if (onSchedule) onSchedule(lineId);
}}
className="flex w-full items-center gap-3 px-4 py-2 text-sm text-foreground hover:bg-accent/10 hover:text-accent transition-colors text-left"
role="menuitem"
>
<Calendar className="w-4 h-4" />
Schedule
</button>
{/* Details */}
<button
onClick={() => {
setIsOpen(false);
if (onDetails) onDetails(lineId);
}}
className="flex w-full items-center gap-3 px-4 py-2 text-sm text-foreground hover:bg-accent/10 hover:text-accent transition-colors text-left"
role="menuitem"
>
<Info className="w-4 h-4" />
Details
</button>
</div>
)}
</div>
);
}