Compare commits

..

No commits in common. "99f40a36895b4ed6cd152eeb255ec44fcd4e7259" and "6344d015dbaa1e07c554eb37949c59d5b583e9e4" have entirely different histories.

5 changed files with 130 additions and 295 deletions

View File

@ -1,5 +1,5 @@
import React, { useState } from 'react'; import React, { useState } from 'react'
import { ContactTypeObj } from './RecipientAddCard'; import { ContactTypeObj } from './RecipientAddCard'
import { Recipient } from '../../../store/slices/selectedRecipientsSlice'; import { Recipient } from '../../../store/slices/selectedRecipientsSlice';
interface ContactDataProps { interface ContactDataProps {
@ -10,127 +10,66 @@ interface ContactDataProps {
openContactModal: boolean; openContactModal: boolean;
setOpenContactModal: React.Dispatch<React.SetStateAction<boolean>>; setOpenContactModal: React.Dispatch<React.SetStateAction<boolean>>;
setRecipients: React.Dispatch<React.SetStateAction<Recipient>>; setRecipients: React.Dispatch<React.SetStateAction<Recipient>>;
topRef?: React.RefObject<HTMLDivElement>;
setManualFocusTriggered: React.Dispatch<React.SetStateAction<boolean>>
} }
// Helper function to highlight matching text const ContactModal: React.FC<ContactDataProps> = ({ contactData, inpFirstName, inpLastName, inpEmail, openContactModal, setRecipients, setOpenContactModal }) => {
const highlightText = (text: string, searchTerm: string, shouldHighlight: boolean) => {
if (!shouldHighlight || !searchTerm.trim()) {
return <span>{text}</span>;
}
const searchTermLower = searchTerm.toLowerCase();
const textLower = text.toLowerCase();
const index = textLower.indexOf(searchTermLower);
if (index === -1) {
return <span>{text}</span>;
}
const beforeMatch = text.slice(0, index);
const match = text.slice(index, index + searchTerm.length);
const afterMatch = text.slice(index + searchTerm.length);
return (
<span>
{beforeMatch}
<span className="bg-yellow-200 font-medium">{match}</span>
{afterMatch}
</span>
);
};
const ContactModal: React.FC<ContactDataProps> = ({
contactData,
inpFirstName,
inpLastName,
inpEmail,
openContactModal,
setRecipients,
setOpenContactModal ,
setManualFocusTriggered
}) => {
const handleContactClick = (indx: number) => { const handleContactClick = (indx: number) => {
if (!contactData.contacts.length) return; if (!contactData.contacts.length) return;
setRecipients((prev) => ({ setRecipients((prev) => {
...prev, // Preserve other properties like uuid, newOne, etc. return {
firstName: contactData.contacts[indx].firstName, firstName: contactData.contacts[indx].firstName,
lastName: contactData.contacts[indx].lastName, lastName: contactData.contacts[indx].lastName,
email: contactData.contacts[indx].email, email: contactData.contacts[indx].email,
})); type: prev.type
}
});
setOpenContactModal(false); setOpenContactModal(false);
setManualFocusTriggered(false)
};
const handleKeyDown = (event: React.KeyboardEvent, indx: number) => {
if (event.key === 'Enter' || event.key === ' ') {
event.preventDefault();
handleContactClick(indx);
}
};
if (!openContactModal || !contactData.contacts.length) {
return null;
} }
return ( return (
<div className="max-h-60 overflow-y-auto"> <>
{contactData.contacts.map((contact, indx) => ( {
<div contactData.contacts.map((recipient, indx) => {
key={contact.id || indx} return <div
tabIndex={0} tabIndex={1}
onMouseDown={() => handleContactClick(indx)} onClick={() => handleContactClick(indx)}
onKeyDown={(e) => handleKeyDown(e, indx)} style={{display: `${openContactModal ? "flex" : "none"}`}}
className="flex items-center px-3 py-2 cursor-pointer hover:bg-gray-50 transition-colors duration-150 focus:bg-gray-100 focus:outline-none rounded-md" className='flex items-center px-3 py-2 cursor-pointer hover:bg-slate-100 transiton-all w-full'>
role="button" <div
aria-label={`Select contact ${contact.firstName} ${contact.lastName}`} key={indx}
> className='w-8 h-8 bg-gray-200 rounded-full'></div>
{/* Avatar */} <div className='ml-2'>
<div className="w-8 h-8 bg-[var(--primaryLight2)] rounded-full flex items-center justify-center text-white text-sm font-medium"> <div className='flex gap-1.5'>
{(contact.firstName?.[0] || '') + (contact.lastName?.[0] || '')} <p className='text-sm font-semibold'
</div> style={{ background: `${contactData.type === "firstName" ? `linear-gradient(to right, yellow 0ch, yellow ${inpFirstName.length}ch, white ${inpFirstName.length}ch, white 100%)` : "white"}` }}
>
{/* Contact Info */} {recipient.firstName}
<div className="ml-3 flex-1 min-w-0"> </p>
<div className="flex gap-1.5 items-center"> <p
<p className="text-sm font-semibold text-gray-900 truncate"> style={{ background: `${contactData.type === "lastName" ? `linear-gradient(to right, yellow 0ch, yellow ${inpLastName.length}ch, white ${inpLastName.length}ch, white 100%)` : "white"}` }}
{highlightText( className='text-sm font-semibold'>
contact.firstName, {recipient.lastName}
inpFirstName, </p>
contactData.type === "firstName" </div>
)} <p className='text-xs text-gray-500'
</p> style={{ background: `${contactData.type === "email" ? `linear-gradient(to right, yellow 0ch, yellow ${inpEmail.length}ch, white ${inpEmail.length}ch, white 100%)` : "white"}` }}
<p className="text-sm font-semibold text-gray-900 truncate"> >
{highlightText( {recipient.email}
contact.lastName,
inpLastName,
contactData.type === "lastName"
)}
</p> </p>
</div> </div>
<p className="text-xs text-gray-500 truncate">
{highlightText(
contact.email,
inpEmail,
contactData.type === "email"
)}
</p>
</div> </div>
</div> })
))} }
</>
)
}
{/* No results message */} // return <p
{contactData.contacts.length === 0 && ( // key={indx}
<div className="px-3 py-4 text-center text-gray-500 text-sm"> // className='w-full hover:bg-slate-100 cursor-pointer px-2 py-1'>
No contacts found // {val.firstName}
</div> // </p>
)} export default ContactModal
</div>
);
};
export default ContactModal;

View File

@ -6,7 +6,7 @@ import SelectedDocuments from "./SelectedDocuments";
import api from "../../../config/api"; import api from "../../../config/api";
import { useDispatch, useSelector } from "react-redux"; import { useDispatch, useSelector } from "react-redux";
import { RootState } from "../../../store/store"; import { RootState } from "../../../store/store";
import { useEffect, useRef, useState } from "react"; import { useEffect, useState } from "react";
import { useNavigate, useParams } from "react-router-dom"; import { useNavigate, useParams } from "react-router-dom";
import { setImages } from "../../../store/slices/selectedDocsSlice"; import { setImages } from "../../../store/slices/selectedDocsSlice";
import { addRecipient, addRecipientAsync, fetchRecipients, recipientType, resetRecipients, toggleToTemplates, TRANSACTION_TEMPLATE_TOGGLE, updateRecipientIds } from "../../../store/slices/selectedRecipientsSlice"; import { addRecipient, addRecipientAsync, fetchRecipients, recipientType, resetRecipients, toggleToTemplates, TRANSACTION_TEMPLATE_TOGGLE, updateRecipientIds } from "../../../store/slices/selectedRecipientsSlice";
@ -39,8 +39,8 @@ const ProcessStepOne = () => {
dispatch(toggleToTemplates()); dispatch(toggleToTemplates());
} }
const [editCase, setEditCase] = useState<string>("") const [editCase , setEditCase] = useState<string>("")
const [editRecipient, setEditRecipient] = useState({}) const [editRecipient , setEditRecipient] = useState({})
@ -55,9 +55,6 @@ const ProcessStepOne = () => {
const selectedDocuments = useSelector((state: RootState) => state.documents.documents) const selectedDocuments = useSelector((state: RootState) => state.documents.documents)
const selectedRecipients = useSelector((state: RootState) => state.selectedRecipients.recipients) const selectedRecipients = useSelector((state: RootState) => state.selectedRecipients.recipients)
const userData = useSelector((state: RootState) => state.user) const userData = useSelector((state: RootState) => state.user)
// ProcessStepOne.tsx
const topRef = useRef<HTMLDivElement | null>(null);
// const userData = useSelector((state: RootState) => state.user) // const userData = useSelector((state: RootState) => state.user)
const [transactionDetails, setTransactionDetails] = useState<any>([]); const [transactionDetails, setTransactionDetails] = useState<any>([]);
@ -65,10 +62,24 @@ const ProcessStepOne = () => {
console.log("selectedRecipients", selectedRecipients) console.log("selectedRecipients", selectedRecipients)
useEffect(() => { useEffect(() => {
<<<<<<< HEAD
// Fetching Transaction Details - START
(async () => {
try {
const res = await api.get(`/api/accounts/${accountId}/transactions/${transactionId}`);
setTransactionDetails(res.data)
} catch (e) {
console.log("Error - transactions details")
}
})()
// Fetching Transaction Details - END
=======
(async () => { (async () => {
const res = await api.get(`/api/accounts/${accountId}/${transactionTemplateToggle}/${transactionId}`) const res = await api.get(`/api/accounts/${accountId}/${transactionTemplateToggle}/${transactionId}`)
console.log(res.data); console.log(res.data);
})() })()
>>>>>>> origin/main
selectedRecipients.length === 0 && dispatch(fetchRecipients({ accountId, transactionId })); selectedRecipients.length === 0 && dispatch(fetchRecipients({ accountId, transactionId }));
selectedDocuments.length === 0 && dispatch(fetchDocuments({ accountId, transactionId })); selectedDocuments.length === 0 && dispatch(fetchDocuments({ accountId, transactionId }));
@ -166,7 +177,7 @@ const ProcessStepOne = () => {
language: "en", language: "en",
name: `${recipient.firstName} ${recipient.lastName}`, name: `${recipient.firstName} ${recipient.lastName}`,
roleName: recipient.roleName, roleName: recipient.roleName,
allowSenderToEdit: recipient.allowSenderToEdit, allowSenderToEdit: recipient.allowSenderToEdit ,
allowSenderToDelete: recipient.allowSenderToDelete, allowSenderToDelete: recipient.allowSenderToDelete,
})); }));
@ -337,17 +348,9 @@ const ProcessStepOne = () => {
{ {
signType !== SignTypeEnum.selfSign && signType !== SignTypeEnum.selfSign &&
<div className="flex flex-col gap-4 align-start"> <div className="flex flex-col gap-4 align-start">
<RecipientCards transactionTemplateToggle={transactionTemplateToggle} setEditRecipient={setEditRecipient} setEditCase={setEditCase} /> <RecipientCards transactionTemplateToggle={transactionTemplateToggle} setEditRecipient={setEditRecipient} setEditCase={setEditCase}/>
{/* {signType !== "self-sign" && <RecipientAddCard />} */} {/* {signType !== "self-sign" && <RecipientAddCard />} */}
<RecipientAddCard transactionTemplateToggle={transactionTemplateToggle} editRecipient={editRecipient} editCase={editCase} setEditRecipient={setEditRecipient} setEditCase={setEditCase}/>
<div
ref={topRef}
className={`relative w-full bg-[var(--mainViewBg)] ${globalLoader.isLoading ? 'pointer-events-none h-screen overflow-hidden' : ''}`}
>
<RecipientAddCard transactionTemplateToggle={transactionTemplateToggle} editRecipient={editRecipient} editCase={editCase} setEditRecipient={setEditRecipient} setEditCase={setEditCase} topRef={topRef} />
</div>
</div> </div>
} }
</section> </section>

View File

@ -24,11 +24,7 @@ export interface ContactTypeObj {
} }
const RecipientAddCard = ({ transactionTemplateToggle, editRecipient, editCase, setEditRecipient, setEditCase, topRef }) => { const RecipientAddCard = ({ transactionTemplateToggle, editRecipient, editCase ,setEditRecipient, setEditCase }) => {
const [activeAccountId, setActiveAccountId] = useState<string | null>(
JSON.parse(sessionStorage.getItem('activeAccountId') || 'null')
);
const [contactData, setContactData] = useState<ContactTypeObj>({ type: "", contacts: [] }); const [contactData, setContactData] = useState<ContactTypeObj>({ type: "", contacts: [] });
const [openContactModal, setOpenContactModal] = useState(false); const [openContactModal, setOpenContactModal] = useState(false);
@ -36,31 +32,11 @@ const RecipientAddCard = ({ transactionTemplateToggle, editRecipient, editCase,
const dispatch = useDispatch<AppDispatch>(); const dispatch = useDispatch<AppDispatch>();
const formRef = useRef<HTMLFormElement | null>(null); const formRef = useRef<HTMLFormElement | null>(null);
const [recipient, setRecipients] = useState<Recipient>({ uuid: null, newOne: false, firstName: "", lastName: "", email: "", hostName: "", hostEmail: "", roleName: "", type: recipientType.SIGNER, allowSenderToEdit: false, allowSenderToDelete: false }); const [recipient, setRecipients] = useState<Recipient>({ uuid:null, newOne:false, firstName: "", lastName: "", email: "", hostName: "", hostEmail: "", roleName: "", type: recipientType.SIGNER, allowSenderToEdit: false, allowSenderToDelete: false });
const [showAdvanceSettings, setShowAdvanceSettings] = useState(false) const [showAdvanceSettings, setShowAdvanceSettings] = useState(false)
const [cachedContacts, setCachedContacts] = useState<ContactType[]>([]);
const [filteredContacts, setFilteredContacts] = useState<ContactType[]>([]);
const [activeField, setActiveField] = useState<"firstName" | "lastName" | "email" | null>(null);
const [hasFetchedInitial, setHasFetchedInitial] = useState(false);
const [lastQuery, setLastQuery] = useState<string | null>(null);
const containerRef = useRef<HTMLDivElement | null>(null);
const [manualFocusTriggered, setManualFocusTriggered] = useState(false);
console.log(recipient) console.log(recipient)
useEffect(() => {
if (openContactModal) {
if (topRef?.current) {
topRef.current.scrollIntoView({ behavior: "smooth", block: "start" });
} else {
window.scrollTo({ top: 0, behavior: "smooth" });
}
}
}, [openContactModal]);
useEffect(() => { useEffect(() => {
if (editCase === "Edit" && editRecipient) { if (editCase === "Edit" && editRecipient) {
setRecipients({ setRecipients({
@ -75,55 +51,56 @@ const RecipientAddCard = ({ transactionTemplateToggle, editRecipient, editCase,
allowSenderToEdit: editRecipient.allowSenderToEdit ?? false, allowSenderToEdit: editRecipient.allowSenderToEdit ?? false,
allowSenderToDelete: editRecipient.allowSenderToDelete ?? false, allowSenderToDelete: editRecipient.allowSenderToDelete ?? false,
uuid: String(editRecipient?.id ?? editRecipient?.uuid ?? ''), uuid: String(editRecipient?.id ?? editRecipient?.uuid ?? ''),
newOne: editRecipient.newOne ?? false newOne:editRecipient.newOne ?? false
}); });
} }
}, [editCase, editRecipient]); }, [editCase, editRecipient]);
const handleRecipientAdd = (e: React.FormEvent<HTMLFormElement>) => { const handleRecipientAdd = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault(); e.preventDefault();
console.log("recipientrecipient", recipient) console.log("recipientrecipient",recipient)
if (!recipient.lastName || !recipient.email || !recipient.type) return; if (!recipient.lastName || !recipient.email || !recipient.type) return;
// ✅ Email format validation // ✅ Email format validation
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(recipient.email)) { if (!emailRegex.test(recipient.email)) {
console.log("Invalid email format"); console.log("Invalid email format");
return; return;
} }
if (editCase === 'Edit') { if (editCase === 'Edit') {
dispatch(updateRecipient(recipient)); dispatch(updateRecipient(recipient));
} else { } else {
dispatch(addRecipient({ dispatch(addRecipient({
...recipient, ...recipient,
uuid: uuidv4(), uuid:uuidv4(),
newOne: true newOne:true
})); }));
} }
setRecipients({ // ✅ Reset state and form
firstName: "", setRecipients({
lastName: "", firstName: "",
email: "", lastName: "",
hostName: "", email: "",
hostEmail: "", hostName: "",
roleName: "", hostEmail: "",
type: recipientType.SIGNER, roleName: "",
allowSenderToEdit: false, type: recipientType.SIGNER,
allowSenderToDelete: false, allowSenderToEdit: false,
uuid: "", allowSenderToDelete: false,
newOne: false uuid:"",
}); newOne:false
});
// setRecipients(null), // setRecipients(null),
setEditCase("") setEditCase("")
setManualFocusTriggered(false)
formRef.current?.reset(); formRef.current?.reset();
}; };
@ -178,97 +155,7 @@ setManualFocusTriggered(false)
}, 350) }, 350)
} }
const handleFocus = async (field: "firstName" | "lastName" | "email") => {
setManualFocusTriggered(true);
setOpenContactModal(true);
setActiveField(field);
setLastQuery(null); // Reset to allow fresh input fetch if needed
if (!hasFetchedInitial) {
try {
const res = await api.get(`/api/accounts/${activeAccountId}/contacts?pageSize=10`);
const contacts = res.data?.content || [];
setCachedContacts(contacts);
setFilteredContacts(contacts);
setContactData({ type: field, contacts });
setHasFetchedInitial(true);
} catch (err) {
console.error("Initial contact fetch failed:", err);
}
} else {
setFilteredContacts(cachedContacts);
setContactData({ type: field, contacts: cachedContacts });
}
};
useDebouncedEffect(() => {
console.log("useDebouncedEffect")
if (!activeField || !manualFocusTriggered) return;
const inputValue = recipient[activeField].trim().toLowerCase();
if (inputValue.length === 0) {
setFilteredContacts(cachedContacts);
setContactData({ type: activeField, contacts: cachedContacts });
setOpenContactModal(true); // Keep showing modal with all options
return;
}
// Local match
const localMatches = cachedContacts.filter(contact =>
contact[activeField]?.toLowerCase().includes(inputValue)
);
if (localMatches.length > 0) {
setFilteredContacts(localMatches);
setContactData({ type: activeField, contacts: localMatches });
setOpenContactModal(true);
return;
}
if (lastQuery !== inputValue) {
setLastQuery(inputValue);
api
.get(`/api/accounts/${activeAccountId}/contacts?pageSize=5&${activeField}=${inputValue}`)
.then((res) => {
const fetched = res.data?.content || [];
setFilteredContacts(fetched);
setContactData({ type: activeField, contacts: fetched });
setOpenContactModal(true);
})
.catch(console.error);
}
}, [recipient.firstName, recipient.lastName, recipient.email], 300);
useEffect(() => {
const handleClickOutside = (e: MouseEvent | FocusEvent) => {
if (
containerRef.current &&
!containerRef.current.contains(e.target as Node)
) {
setOpenContactModal(false);
}
};
document.addEventListener("mousedown", handleClickOutside);
document.addEventListener("focusin", handleClickOutside); // Also handles tabbing out
return () => {
document.removeEventListener("mousedown", handleClickOutside);
document.removeEventListener("focusin", handleClickOutside);
};
}, []);
return ( return (
<div ref={containerRef}>
<form <form
ref={formRef} ref={formRef}
onSubmit={handleRecipientAdd} onSubmit={handleRecipientAdd}
@ -404,7 +291,7 @@ useEffect(() => {
<div className="grid grid-cols-12 gap-4 mt-3"> <div className="grid grid-cols-12 gap-4 mt-3">
<input <input
type="text" type="text"
onFocus={() => handleFocus("firstName")} onFocus={() => setOpenContactModal(true)}
onBlur={handleContactModalBlur} onBlur={handleContactModalBlur}
value={recipient.firstName} value={recipient.firstName}
placeholder="First Name" placeholder="First Name"
@ -414,7 +301,7 @@ useEffect(() => {
<input <input
required required
type="text" type="text"
onFocus={() => handleFocus("lastName")} onFocus={() => setOpenContactModal(true)}
onBlur={handleContactModalBlur} onBlur={handleContactModalBlur}
value={recipient.lastName} value={recipient.lastName}
placeholder="Last Name" placeholder="Last Name"
@ -424,7 +311,7 @@ useEffect(() => {
<input <input
required required
type="email" type="email"
onFocus={() => handleFocus("email")} onFocus={() => setOpenContactModal(true)}
onBlur={handleContactModalBlur} onBlur={handleContactModalBlur}
value={recipient.email} value={recipient.email}
placeholder="Recipient Email" placeholder="Recipient Email"
@ -458,8 +345,8 @@ useEffect(() => {
</div>} </div>}
{/* Contact Modal */} {/* Contact Modal */}
{openContactModal && contactData.contacts.length > 0 && ( {contactData.contacts.length > 0 && openContactModal && (
<div className="absolute z-50 mt-1 w-2/3 max-h-[50vh] bg-white shadow-lg rounded-md p-3 border border-gray-100 overflow-y-auto no-scrollbar"> <div className="absolute z-50 mt-1 w-full max-w-2xl bg-white shadow-lg rounded-md p-3 border border-gray-100">
<ContactModal <ContactModal
contactData={contactData} contactData={contactData}
inpFirstName={recipient.firstName} inpFirstName={recipient.firstName}
@ -468,14 +355,10 @@ useEffect(() => {
openContactModal={openContactModal} openContactModal={openContactModal}
setRecipients={setRecipients} setRecipients={setRecipients}
setOpenContactModal={setOpenContactModal} setOpenContactModal={setOpenContactModal}
topRef={topRef}
setManualFocusTriggered={setManualFocusTriggered}
/> />
</div> </div>
)} )}
{/* Signing options + Add button in one line */} {/* Signing options + Add button in one line */}
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4"> <div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4">
<div className="flex flex-wrap gap-6"> <div className="flex flex-wrap gap-6">
@ -502,11 +385,11 @@ useEffect(() => {
type="submit" type="submit"
className="inline-flex items-center gap-2 bg-[var(--primary)] hover:bg-[var(--primaryLight2)] text-white text-sm font-semibold px-5 py-2 rounded-md transition" className="inline-flex items-center gap-2 bg-[var(--primary)] hover:bg-[var(--primaryLight2)] text-white text-sm font-semibold px-5 py-2 rounded-md transition"
> >
<FaPlus size={14} /> {editCase === "Edit" ? "Edit Recipient" : "Add Recipient"} <FaPlus size={14} /> {editCase === "Edit" ? "Edit Recipient" : "Add Recipient"}
</button> </button>
</div> </div>
</form> </form>
</div>
); );
}; };

View File

@ -59,6 +59,15 @@ const RecipientCards = ({ setEditRecipient, setEditCase, transactionTemplateTogg
const handleRemoveRecipient = (recipient) => { const handleRemoveRecipient = (recipient) => {
if (!accountId || !transactionId) return; if (!accountId || !transactionId) return;
<<<<<<< HEAD
// dispatch(removeSelectedRecipient(recipient))
dispatch(removeRecipientAsync([{
recipientId: recipient.id,
email: recipient.email,
accountId: accountId,
transactionId: transactionId,
}]))
=======
if (recipient.uuid) { if (recipient.uuid) {
dispatch(removeSelectedRecipient(recipient)) dispatch(removeSelectedRecipient(recipient))
} else { } else {
@ -69,6 +78,7 @@ const RecipientCards = ({ setEditRecipient, setEditCase, transactionTemplateTogg
transactionId: transactionId, transactionId: transactionId,
})) }))
} }
>>>>>>> origin/main
if (recipients.length === 2) { if (recipients.length === 2) {
let leftRecipient = recipients.filter((obj) => { let leftRecipient = recipients.filter((obj) => {
if ("id" in obj && "id" in recipient) { if ("id" in obj && "id" in recipient) {

View File

@ -157,7 +157,7 @@ const DirectionalControls: React.FC<DirectionalControlsProps> = ({
</div> </div>
{/* Center Panel */} {/* Center Panel */}
<div className="relative flex items-center justify-center bg-white rounded-lg px-1 py-2 w-24 "> <div className="relative flex items-center justify-center bg-white rounded-lg px-1 py-2 w-24">
{/* Floating dimensions tooltip */} {/* Floating dimensions tooltip */}
<div className="absolute -top-2 left-1/2 -translate-x-1/2 px-2 py-[1px] text-[10px] font-mono text-black z-10 rounded-sm flex items-center gap-[2px]"> <div className="absolute -top-2 left-1/2 -translate-x-1/2 px-2 py-[1px] text-[10px] font-mono text-black z-10 rounded-sm flex items-center gap-[2px]">
<input <input
@ -167,7 +167,7 @@ const DirectionalControls: React.FC<DirectionalControlsProps> = ({
onBlur={() => handlePositionInputBlur('left')} onBlur={() => handlePositionInputBlur('left')}
onKeyPress={(e) => handleKeyPress(e, 'left')} onKeyPress={(e) => handleKeyPress(e, 'left')}
disabled={disabled} disabled={disabled}
className="w-[40px] h-[16px] text-[10px] text-center border-none focus:outline-none text-black font-mono" className="w-[40px] h-[16px] text-[10px] text-center border-none focus:outline-none text-black font-mono"
/> />
<span className="mx-[1px]">×</span> <span className="mx-[1px]">×</span>
<input <input