feat: color picker panel with 18 preset colors + native color input + hex field
This commit is contained in:
@@ -226,6 +226,7 @@ export function ContactFolderTree({
|
||||
const { t } = useTranslation();
|
||||
const [tagsOpen, setTagsOpen] = useState(true);
|
||||
const [dropdown, setDropdown] = useState<DropdownState | null>(null);
|
||||
const [colorPicker, setColorPicker] = useState<{ folderId: string; color: string } | null>(null);
|
||||
const [dragOverFolderId, setDragOverFolderId] = useState<string | null>(null);
|
||||
|
||||
const { data: folders, isLoading: foldersLoading } = useContactFolders();
|
||||
@@ -273,9 +274,13 @@ export function ContactFolderTree({
|
||||
};
|
||||
|
||||
const handleColor = (id: string) => {
|
||||
const color = prompt('Farbe (Hex-Code, z.B. #ff0000):', '#3b82f6');
|
||||
if (!color) return;
|
||||
updateFolderMut.mutate({ id, data: { color } as any });
|
||||
setColorPicker({ folderId: id, color: folderList.find((f) => f.id === id && (f as any).color) ? (folderList.find((f) => f.id === id) as any).color : '#3b82f6' });
|
||||
};
|
||||
|
||||
const handleColorSelect = (color: string) => {
|
||||
if (!colorPicker) return;
|
||||
updateFolderMut.mutate({ id: colorPicker.folderId, data: { color } as any });
|
||||
setColorPicker(null);
|
||||
};
|
||||
|
||||
const handlePin = (id: string) => {
|
||||
@@ -444,6 +449,48 @@ export function ContactFolderTree({
|
||||
onPin={handlePin}
|
||||
/>
|
||||
)}
|
||||
|
||||
{colorPicker && createPortal(
|
||||
<>
|
||||
<div className="fixed inset-0 z-[9998]" onClick={() => setColorPicker(null)} />
|
||||
<div className="fixed z-[9999] bg-white border border-secondary-200 rounded-lg shadow-lg p-3 min-w-[200px]" style={{ top: '50%', left: '50%', transform: 'translate(-50%, -50%)' }}>
|
||||
<div className="text-sm font-semibold text-secondary-700 mb-2">Farbe wählen</div>
|
||||
<div className="grid grid-cols-6 gap-1.5 mb-3">
|
||||
{['#ef4444', '#f97316', '#f59e0b', '#eab308', '#84cc16', '#22c55e', '#10b981', '#14b8a6', '#06b6d4', '#3b82f6', '#6366f1', '#8b5cf6', '#a855f7', '#d946ef', '#ec4899', '#f43f5e', '#64748b', '#475569'].map((c) => (
|
||||
<button
|
||||
key={c}
|
||||
type="button"
|
||||
onClick={() => handleColorSelect(c)}
|
||||
className="w-7 h-7 rounded-md border-2 border-white shadow-sm hover:scale-110 transition-transform"
|
||||
style={{ backgroundColor: c, outline: colorPicker.color === c ? '2px solid #3b82f6' : 'none' }}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<input
|
||||
type="color"
|
||||
value={colorPicker.color}
|
||||
onChange={(e) => setColorPicker({ ...colorPicker, color: e.target.value })}
|
||||
className="w-8 h-8 rounded cursor-pointer border border-secondary-200"
|
||||
/>
|
||||
<input
|
||||
type="text"
|
||||
value={colorPicker.color}
|
||||
onChange={(e) => setColorPicker({ ...colorPicker, color: e.target.value })}
|
||||
className="flex-1 px-2 py-1 text-sm border border-secondary-200 rounded-md"
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => handleColorSelect(colorPicker.color)}
|
||||
className="px-3 py-1 text-sm bg-primary-600 text-white rounded-md hover:bg-primary-700"
|
||||
>
|
||||
OK
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</>,
|
||||
document.body
|
||||
)}
|
||||
</nav>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user