File: /home/parhudrw/luca.anqa.it/wp-content/plugins/extendify/src/Agent/components/OptionsPopover.jsx
import { usePortal } from '@agent/hooks/usePortal';
import animationWorkflow from '@agent/workflows/theme/change-animation';
import vibesWorkflow from '@agent/workflows/theme/change-site-vibes';
import fontsWorkflow from '@agent/workflows/theme/change-theme-fonts-variation';
import variationWorkflow from '@agent/workflows/theme/change-theme-variation';
import { createPortal, useEffect, useRef, useState } from '@wordpress/element';
import { __ } from '@wordpress/i18n';
import { Icon, moreVertical } from '@wordpress/icons';
import { AnimatePresence, motion } from 'framer-motion';
export const OptionsPopover = () => {
const popoutNode = usePortal('extendify-agent-border-frame-mount');
const buttonRef = useRef(null);
const firstButtonRef = useRef(null);
const [topRight, setTopRight] = useState(null);
const onOpen = () => {
if (topRight) return setTopRight(null);
const rect = buttonRef.current.getBoundingClientRect();
setTopRight({
top: rect.bottom + 4,
right: window.innerWidth - rect.right,
});
};
const onClose = () => {
setTopRight(null);
buttonRef.current?.focus();
};
useEffect(() => {
if (!topRight || !firstButtonRef.current) return;
firstButtonRef.current.focus();
}, [topRight]);
useEffect(() => {
// click away
const handle = (e) => {
if (buttonRef.current.contains(e.target)) return;
if (popoutNode?.contains(e.target)) return;
setTopRight(null);
};
window.addEventListener('click', handle);
return () => window.removeEventListener('click', handle);
}, [popoutNode]);
if (!popoutNode) return null;
return (
<>
{createPortal(
topRight ? (
<AnimatePresence>
<Popover
position={topRight}
onClose={onClose}
firstItemRef={firstButtonRef}
/>
</AnimatePresence>
) : null,
popoutNode,
)}
<button
ref={buttonRef}
type="button"
className="relative z-10 flex justify-center h-6 w-6 items-center border-0 bg-banner-main text-banner-text outline-hidden ring-design-main focus:shadow-none focus:outline-hidden focus-visible:outline-design-main focus:ring-2 hover:bg-gray-100 rounded-md"
onClick={onOpen}
aria-expanded={topRight ? 'true' : 'false'}
aria-haspopup="true"
>
<Icon
className="pointer-events-none fill-current leading-none"
icon={moreVertical}
size={18}
/>
<span className="sr-only">
{__('View more options', 'extendify-local')}
</span>
</button>
</>
);
};
const buttons = [
{ name: fontsWorkflow.example.text, available: fontsWorkflow.available },
{
name: variationWorkflow.example.text,
available: variationWorkflow.available,
},
{ name: vibesWorkflow.example.text, available: vibesWorkflow.available },
{
name: animationWorkflow.example.text,
available: animationWorkflow.available,
},
];
const Popover = ({ position, onClose, firstItemRef }) => {
useEffect(() => {
const handle = (e) => {
if (e.key === 'Escape') {
e.preventDefault();
onClose();
}
};
window.addEventListener('keydown', handle);
return () => window.removeEventListener('keydown', handle);
}, [onClose]);
return (
<motion.div
className="fixed max-w-50 rounded-md bg-white shadow-xl py-2 z-max flex flex-col items-start justify-center gap-1 focus:outline-none border border-gray-300"
style={{ top: position.top, right: position.right }}
initial={{ opacity: 0, scale: 0.95 }}
animate={{ opacity: 1, scale: 1 }}
exit={{ opacity: 0, scale: 0.95 }}
transition={{ duration: 0.2 }}
onKeyDown={(e) => {
e.stopPropagation();
e.preventDefault();
if (e.key === 'Escape') return onClose();
const curr = document.activeElement;
if (e.key === 'ArrowDown' || (e.key === 'Tab' && !e.shiftKey)) {
if (curr.nextSibling) {
return curr.nextSibling.focus();
}
return firstItemRef.current.focus();
}
if (e.key === 'ArrowUp' || (e.key === 'Tab' && e.shiftKey)) {
if (curr.previousSibling) {
return curr.previousSibling.focus();
}
return curr.parentNode.lastChild.focus();
}
if (e.key === 'Enter') {
return curr.click();
}
}}
>
{buttons
.filter(({ available }) => available())
.map(({ name }, i) => (
<button
key={name}
type="button"
className="w-full px-4 py-2 text-sm text-gray-900 text-left hover:bg-gray-100 focus:ring-2 focus:ring-design-main focus:outline-none"
ref={i === 0 ? firstItemRef : null}
role="menuitem"
onClick={() => {
window.dispatchEvent(
new CustomEvent('extendify-agent:chat-submit', {
detail: { message: name },
}),
);
onClose();
}}
>
{name}
</button>
))}
</motion.div>
);
};