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 { t } = useTranslation();
|
||||||
const [tagsOpen, setTagsOpen] = useState(true);
|
const [tagsOpen, setTagsOpen] = useState(true);
|
||||||
const [dropdown, setDropdown] = useState<DropdownState | null>(null);
|
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 [dragOverFolderId, setDragOverFolderId] = useState<string | null>(null);
|
||||||
|
|
||||||
const { data: folders, isLoading: foldersLoading } = useContactFolders();
|
const { data: folders, isLoading: foldersLoading } = useContactFolders();
|
||||||
@@ -273,9 +274,13 @@ export function ContactFolderTree({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleColor = (id: string) => {
|
const handleColor = (id: string) => {
|
||||||
const color = prompt('Farbe (Hex-Code, z.B. #ff0000):', '#3b82f6');
|
setColorPicker({ folderId: id, color: folderList.find((f) => f.id === id && (f as any).color) ? (folderList.find((f) => f.id === id) as any).color : '#3b82f6' });
|
||||||
if (!color) return;
|
};
|
||||||
updateFolderMut.mutate({ id, data: { color } as any });
|
|
||||||
|
const handleColorSelect = (color: string) => {
|
||||||
|
if (!colorPicker) return;
|
||||||
|
updateFolderMut.mutate({ id: colorPicker.folderId, data: { color } as any });
|
||||||
|
setColorPicker(null);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handlePin = (id: string) => {
|
const handlePin = (id: string) => {
|
||||||
@@ -444,6 +449,48 @@ export function ContactFolderTree({
|
|||||||
onPin={handlePin}
|
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>
|
</nav>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user