Seth commited on
Commit
9faed2a
·
1 Parent(s): b52b265
frontend/src/components/workspace/SearchableCountryPicker.jsx CHANGED
@@ -13,7 +13,7 @@ export function SearchableCountryPicker({ value, onChange, className = '' }) {
13
  const triggerRef = useRef(null);
14
  const menuRef = useRef(null);
15
  const inputRef = useRef(null);
16
- const [pos, setPos] = useState({ top: 0, left: 0, width: 0 });
17
 
18
  const matched = matchCountry(value || '', countries);
19
  const flag = matched ? flagEmojiFromCode(matched.code) : '🏳️';
@@ -28,19 +28,37 @@ export function SearchableCountryPicker({ value, onChange, className = '' }) {
28
  );
29
  }, [countries, query]);
30
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
31
  useLayoutEffect(() => {
32
  if (!open || !triggerRef.current) return;
33
- const r = triggerRef.current.getBoundingClientRect();
34
- setPos({ top: r.bottom + 4, left: r.left, width: Math.max(r.width, 220) });
35
  }, [open]);
36
 
37
  useEffect(() => {
38
  if (!open) return;
39
- const reposition = () => {
40
- if (!triggerRef.current) return;
41
- const r = triggerRef.current.getBoundingClientRect();
42
- setPos({ top: r.bottom + 4, left: r.left, width: Math.max(r.width, 220) });
43
- };
44
  window.addEventListener('scroll', reposition, true);
45
  window.addEventListener('resize', reposition);
46
  return () => {
@@ -102,18 +120,15 @@ export function SearchableCountryPicker({ value, onChange, className = '' }) {
102
  ref={menuRef}
103
  role="listbox"
104
  data-country-picker="true"
105
- className="fixed z-[10050] rounded-md border border-slate-200 bg-white shadow-lg"
106
  style={{
107
  top: pos.top,
108
  left: pos.left,
109
- minWidth: pos.width,
110
- maxHeight: 'min(70vh, 20rem)',
111
- display: 'flex',
112
- flexDirection: 'column',
113
  }}
114
  onClick={(e) => e.stopPropagation()}
115
  >
116
- <div className="border-b border-slate-100 p-2">
117
  <input
118
  ref={inputRef}
119
  type="search"
@@ -126,7 +141,7 @@ export function SearchableCountryPicker({ value, onChange, className = '' }) {
126
  className="w-full rounded-md border border-slate-200 px-2 py-1.5 text-sm outline-none focus:border-violet-300 focus:ring-1 focus:ring-violet-200"
127
  />
128
  </div>
129
- <div className="overflow-y-auto p-1">
130
  <button
131
  type="button"
132
  role="option"
 
13
  const triggerRef = useRef(null);
14
  const menuRef = useRef(null);
15
  const inputRef = useRef(null);
16
+ const [pos, setPos] = useState({ top: 0, left: 0, width: 320 });
17
 
18
  const matched = matchCountry(value || '', countries);
19
  const flag = matched ? flagEmojiFromCode(matched.code) : '🏳️';
 
28
  );
29
  }, [countries, query]);
30
 
31
+ const computeMenuPosition = () => {
32
+ const trigger = triggerRef.current;
33
+ if (!trigger) return;
34
+ const r = trigger.getBoundingClientRect();
35
+ const PAD = 12;
36
+ const MENU_W = 320;
37
+ const vw = window.innerWidth;
38
+ const vh = window.innerHeight;
39
+ // Last-column friendly: if trigger sits in the right part of the viewport,
40
+ // anchor the panel by its right edge to the trigger's right edge so the menu opens leftward.
41
+ const inRightZone = r.right > vw * 0.55;
42
+ let left = inRightZone ? r.right - MENU_W : r.left;
43
+ left = Math.max(PAD, Math.min(left, vw - MENU_W - PAD));
44
+
45
+ const estPanelH = Math.min(vh * 0.72, 420);
46
+ let top = r.bottom + 6;
47
+ if (top + estPanelH > vh - PAD) {
48
+ top = Math.max(PAD, r.top - estPanelH - 6);
49
+ }
50
+
51
+ setPos({ top, left, width: MENU_W });
52
+ };
53
+
54
  useLayoutEffect(() => {
55
  if (!open || !triggerRef.current) return;
56
+ computeMenuPosition();
 
57
  }, [open]);
58
 
59
  useEffect(() => {
60
  if (!open) return;
61
+ const reposition = () => computeMenuPosition();
 
 
 
 
62
  window.addEventListener('scroll', reposition, true);
63
  window.addEventListener('resize', reposition);
64
  return () => {
 
120
  ref={menuRef}
121
  role="listbox"
122
  data-country-picker="true"
123
+ className="fixed z-[10050] flex max-h-[min(70vh,24rem)] flex-col rounded-md border border-slate-200 bg-white shadow-xl ring-1 ring-black/5"
124
  style={{
125
  top: pos.top,
126
  left: pos.left,
127
+ width: pos.width,
 
 
 
128
  }}
129
  onClick={(e) => e.stopPropagation()}
130
  >
131
+ <div className="shrink-0 border-b border-slate-100 p-2">
132
  <input
133
  ref={inputRef}
134
  type="search"
 
141
  className="w-full rounded-md border border-slate-200 px-2 py-1.5 text-sm outline-none focus:border-violet-300 focus:ring-1 focus:ring-violet-200"
142
  />
143
  </div>
144
+ <div className="min-h-0 flex-1 overflow-y-auto overscroll-contain p-1">
145
  <button
146
  type="button"
147
  role="option"
frontend/src/pages/Deals.jsx CHANGED
@@ -277,7 +277,7 @@ function DealRow({ deal, tableEditRowId, setTableEditRowId, patchDeal, updateSta
277
  </div>
278
  )}
279
  </td>
280
- <td className="px-3 py-2 align-top tabular-nums max-w-[120px]">
281
  {tableEditRowId === deal.id ? (
282
  <EditableCell
283
  type="number"
@@ -291,6 +291,8 @@ function DealRow({ deal, tableEditRowId, setTableEditRowId, patchDeal, updateSta
291
  if (!Number.isFinite(n)) return;
292
  patchDeal(deal.id, { deal_value: n });
293
  }}
 
 
294
  />
295
  ) : (
296
  <div className="min-h-[2rem] py-1 text-sm text-slate-700 tabular-nums">
 
277
  </div>
278
  )}
279
  </td>
280
+ <td className="px-3 py-2 align-top tabular-nums min-w-[10rem] w-40 max-w-[16rem]">
281
  {tableEditRowId === deal.id ? (
282
  <EditableCell
283
  type="number"
 
291
  if (!Number.isFinite(n)) return;
292
  patchDeal(deal.id, { deal_value: n });
293
  }}
294
+ className="w-full"
295
+ inputClassName="min-w-[9rem] w-full max-w-full tabular-nums text-right text-base py-1"
296
  />
297
  ) : (
298
  <div className="min-h-[2rem] py-1 text-sm text-slate-700 tabular-nums">