contacts toolbar: consolidate views and filters into dropdowns

This commit is contained in:
Agent Zero
2026-07-28 00:45:55 +02:00
parent 5378372aba
commit 722335c923
3 changed files with 93 additions and 56 deletions
@@ -85,6 +85,71 @@ function ToolbarSearch({ item }: { item: ToolbarItem }) {
);
}
function ToolbarDropdown({ item }: { item: ToolbarItem }) {
const [open, setOpen] = React.useState(false);
const ref = React.useRef<HTMLDivElement>(null);
React.useEffect(() => {
if (!open) return;
const handler = (e: MouseEvent) => {
if (ref.current && !ref.current.contains(e.target as Node)) setOpen(false);
};
document.addEventListener('mousedown', handler);
return () => document.removeEventListener('mousedown', handler);
}, [open]);
const activeOption = item.menuOptions?.find((o) => o.active);
return (
<div ref={ref} className="relative flex-shrink-0">
<button
onClick={() => setOpen(!open)}
title={item.label}
aria-label={item.label}
className={`
inline-flex items-center gap-1.5 px-2 py-1 rounded text-xs font-medium
transition-colors duration-100 cursor-pointer
${open || activeOption ? 'bg-primary-100 text-primary-700' : 'text-secondary-600 hover:bg-secondary-100 hover:text-secondary-900'}
`}
>
{item.icon && <span className="w-4 h-4 flex-shrink-0">{item.icon}</span>}
<span className="hidden sm:inline">{activeOption?.label || item.label}</span>
<svg className={`w-3 h-3 transition-transform ${open ? 'rotate-180' : ''}`} fill="none" stroke="currentColor" strokeWidth={2} viewBox="0 0 24 24">
<polyline points="6 9 12 15 18 9" />
</svg>
</button>
{open && (
<div
className="absolute top-full left-0 mt-1 bg-white border border-secondary-200 rounded-lg shadow-lg z-50 py-1"
style={{ minWidth: item.menuWidth || '180px' }}
>
{item.menuOptions?.map((opt) => (
<button
key={opt.value}
onClick={() => {
opt.onClick();
setOpen(false);
}}
className={`
w-full flex items-center gap-2 px-3 py-1.5 text-xs text-left transition-colors
${opt.active ? 'bg-primary-50 text-primary-700 font-medium' : 'text-secondary-700 hover:bg-secondary-100'}
`}
>
{opt.icon && <span className="w-4 h-4 flex-shrink-0">{opt.icon}</span>}
<span className="flex-1">{opt.label}</span>
{opt.active && (
<svg className="w-3.5 h-3.5 text-primary-600" fill="none" stroke="currentColor" strokeWidth={2.5} viewBox="0 0 24 24">
<polyline points="20 6 9 17 4 12" />
</svg>
)}
</button>
))}
</div>
)}
</div>
);
}
function ToolbarSelect({ item }: { item: ToolbarItem }) {
return (
<select
@@ -128,6 +193,7 @@ export function PluginToolbar() {
{groups[groupName].map((item) => {
if (item.type === 'search') return <ToolbarSearch key={item.id} item={item} />;
if (item.type === 'select') return <ToolbarSelect key={item.id} item={item} />;
if (item.type === 'dropdown') return <ToolbarDropdown key={item.id} item={item} />;
return <ToolbarButton key={item.id} item={item} />;
})}
</div>