120 lines
4.8 KiB
JavaScript
120 lines
4.8 KiB
JavaScript
(()=>{
|
|
const roots=[...document.querySelectorAll('[data-voip-call]')];
|
|
if(!roots.length)return;
|
|
|
|
const csrfToken=()=>{
|
|
const item=document.cookie.split(';').map(value=>value.trim()).find(value=>value.startsWith('csrftoken='));
|
|
return item?decodeURIComponent(item.slice('csrftoken='.length)):'';
|
|
};
|
|
const menuFor=root=>root._voipMenu;
|
|
const statusFor=root=>root._voipStatus;
|
|
|
|
const positionMenu=root=>{
|
|
const menu=menuFor(root);
|
|
const trigger=root.querySelector('[data-voip-call-trigger]');
|
|
if(!menu||menu.hidden)return;
|
|
if(window.matchMedia('(max-width:560px)').matches){
|
|
menu.style.removeProperty('top');
|
|
menu.style.removeProperty('left');
|
|
return;
|
|
}
|
|
const rect=trigger.getBoundingClientRect();
|
|
const gap=10;
|
|
const edge=12;
|
|
const width=menu.offsetWidth;
|
|
const height=menu.offsetHeight;
|
|
const top=rect.bottom+gap+height<=window.innerHeight-edge?rect.bottom+gap:Math.max(edge,rect.top-height-gap);
|
|
const left=Math.min(Math.max(edge,rect.right-width),window.innerWidth-width-edge);
|
|
menu.style.top=`${Math.round(top)}px`;
|
|
menu.style.left=`${Math.round(left)}px`;
|
|
};
|
|
|
|
const setOpen=(root,open,{restoreFocus=false}={})=>{
|
|
const menu=menuFor(root);
|
|
const trigger=root.querySelector('[data-voip-call-trigger]');
|
|
if(!menu||!trigger)return;
|
|
menu.hidden=!open;
|
|
menu.classList.toggle('is-open',open);
|
|
trigger.classList.toggle('is-open',open);
|
|
trigger.setAttribute('aria-expanded',String(open));
|
|
if(open){
|
|
positionMenu(root);
|
|
requestAnimationFrame(()=>menu.querySelector('[data-voip-method]')?.focus());
|
|
}else{
|
|
menu.style.removeProperty('top');
|
|
menu.style.removeProperty('left');
|
|
if(restoreFocus)trigger.focus();
|
|
}
|
|
};
|
|
|
|
const closeOthers=current=>roots.forEach(root=>{if(root!==current)setOpen(root,false);});
|
|
|
|
const notify=(root,message,state)=>{
|
|
const status=statusFor(root);
|
|
clearTimeout(status._hideTimer);
|
|
status.textContent=message;
|
|
status.className=`voip-call-status is-visible is-${state}`;
|
|
if(state!=='loading')status._hideTimer=setTimeout(()=>status.classList.remove('is-visible'),4200);
|
|
};
|
|
|
|
const call=async(root,method)=>{
|
|
const trigger=root.querySelector('[data-voip-call-trigger]');
|
|
const buttons=[trigger,...menuFor(root).querySelectorAll('button')];
|
|
setOpen(root,false);
|
|
buttons.forEach(button=>button.disabled=true);
|
|
trigger.classList.add('is-loading');
|
|
notify(root,'در حال ارسال درخواست تماس…','loading');
|
|
try{
|
|
const response=await fetch(root.dataset.callUrl,{method:'POST',credentials:'same-origin',headers:{'Content-Type':'application/json','X-CSRFToken':csrfToken(),'Accept':'application/json'},body:JSON.stringify({method})});
|
|
const data=await response.json().catch(()=>({}));
|
|
if(!response.ok||!data.success)throw new Error();
|
|
notify(root,data.message||'درخواست تماس با موفقیت ارسال شد.','success');
|
|
trigger.classList.add('is-success');
|
|
setTimeout(()=>trigger.classList.remove('is-success'),1800);
|
|
}catch(error){
|
|
notify(root,'برقراری تماس ناموفق بود.','error');
|
|
}finally{
|
|
trigger.classList.remove('is-loading');
|
|
buttons.forEach(button=>button.disabled=false);
|
|
}
|
|
};
|
|
|
|
roots.forEach((root,index)=>{
|
|
const menu=root.querySelector('[data-voip-call-menu]');
|
|
const status=root.querySelector('[data-voip-call-status]');
|
|
const trigger=root.querySelector('[data-voip-call-trigger]');
|
|
menu.id=`voip-call-menu-${index+1}`;
|
|
trigger.setAttribute('aria-controls',menu.id);
|
|
root._voipMenu=menu;
|
|
root._voipStatus=status;
|
|
menu._voipRoot=root;
|
|
document.body.append(menu,status);
|
|
});
|
|
|
|
document.addEventListener('click',event=>{
|
|
const methodButton=event.target.closest('[data-voip-method]');
|
|
if(methodButton){
|
|
const root=methodButton.closest('[data-voip-call-menu]')._voipRoot;
|
|
call(root,methodButton.dataset.voipMethod);
|
|
return;
|
|
}
|
|
const trigger=event.target.closest('[data-voip-call-trigger]');
|
|
if(trigger){
|
|
const root=trigger.closest('[data-voip-call]');
|
|
const methods=menuFor(root).querySelectorAll('[data-voip-method]');
|
|
if(methods.length===1){call(root,methods[0].dataset.voipMethod);return;}
|
|
const opening=menuFor(root).hidden;
|
|
closeOthers(root);
|
|
setOpen(root,opening);
|
|
return;
|
|
}
|
|
if(!event.target.closest('[data-voip-call-menu]'))roots.forEach(root=>setOpen(root,false));
|
|
});
|
|
|
|
document.addEventListener('keydown',event=>{
|
|
if(event.key==='Escape')roots.forEach(root=>{if(!menuFor(root).hidden)setOpen(root,false,{restoreFocus:true});});
|
|
});
|
|
window.addEventListener('resize',()=>roots.forEach(positionMenu));
|
|
window.addEventListener('scroll',()=>roots.forEach(positionMenu),true);
|
|
})();
|