// Shared mobile-friendly colour picker — used by EventTypesPanel and MobileEventForm // Renders inline (no sheet wrapper) so callers can embed it wherever they like. import { useState, useEffect, useRef } from 'react'; const COLOUR_SUGGESTIONS = [ '#1a73e8','#a142f4','#e53935','#fa7b17','#34a853','#00bcd4', '#ff5722','#795548','#607d8b','#e91e63','#9c27b0','#3f51b5', ]; function hexToHsv(hex) { const r=parseInt(hex.slice(1,3),16)/255, g=parseInt(hex.slice(3,5),16)/255, b=parseInt(hex.slice(5,7),16)/255; const max=Math.max(r,g,b), min=Math.min(r,g,b), d=max-min; let h=0; if(d!==0){if(max===r)h=((g-b)/d+(gMath.round(x*255).toString(16).padStart(2,'0')).join(''); } function isValidHex(h){return/^#[0-9a-fA-F]{6}$/.test(h);} function SvSquare({hue,s,v,onChange}){ const canvasRef=useRef(null);const dragging=useRef(false); useEffect(()=>{ const canvas=canvasRef.current;if(!canvas)return; const ctx=canvas.getContext('2d'),W=canvas.width,H=canvas.height; const hGrad=ctx.createLinearGradient(0,0,W,0);hGrad.addColorStop(0,'#fff');hGrad.addColorStop(1,`hsl(${hue},100%,50%)`); ctx.fillStyle=hGrad;ctx.fillRect(0,0,W,H); const vGrad=ctx.createLinearGradient(0,0,0,H);vGrad.addColorStop(0,'transparent');vGrad.addColorStop(1,'#000'); ctx.fillStyle=vGrad;ctx.fillRect(0,0,W,H); },[hue]); const getPos=(e,canvas)=>{ const r=canvas.getBoundingClientRect(); const cx=(e.touches?e.touches[0].clientX:e.clientX)-r.left; const cy=(e.touches?e.touches[0].clientY:e.clientY)-r.top; return{s:Math.max(0,Math.min(1,cx/r.width)),v:Math.max(0,Math.min(1,1-cy/r.height))}; }; const handle=(e)=>{e.preventDefault();const p=getPos(e,canvasRef.current);onChange(p.s,p.v);}; return(
{dragging.current=true;handle(e);}} onMouseMove={e=>{if(dragging.current)handle(e);}} onMouseUp={()=>{dragging.current=false;}} onMouseLeave={()=>{dragging.current=false;}} onTouchStart={handle} onTouchMove={handle}/>
); } function HueBar({hue,onChange}){ const barRef=useRef(null);const dragging=useRef(false); const handle=(e)=>{ e.preventDefault();const r=barRef.current.getBoundingClientRect(); const cx=(e.touches?e.touches[0].clientX:e.clientX)-r.left; onChange(Math.max(0,Math.min(360,(cx/r.width)*360))); }; return(
{dragging.current=true;handle(e);}} onMouseMove={e=>{if(dragging.current)handle(e);}} onMouseUp={()=>{dragging.current=false;}} onMouseLeave={()=>{dragging.current=false;}} onTouchStart={handle} onTouchMove={handle}/>
); } // Full inline picker — no sheet wrapper, callers handle the container export function ColourPicker({ value, onChange }) { const {h:ih,s:is,v:iv}=hexToHsv(value||'#6366f1'); const [mode,setMode]=useState('suggestions'); // 'suggestions' | 'custom' const [hue,setHue]=useState(ih); const [sat,setSat]=useState(is); const [val,setVal]=useState(iv); const [hexInput,setHexInput]=useState(value||'#6366f1'); const [hexError,setHexError]=useState(false); const current=hsvToHex(hue,sat,val); // Sync from value prop when it changes externally useEffect(()=>{ if(value&&isValidHex(value)){ const{h,s,v}=hexToHsv(value); setHue(h);setSat(s);setVal(v);setHexInput(value); } },[value]); useEffect(()=>{setHexInput(current);setHexError(false);},[current]); const handleHexInput=(e)=>{ const v=e.target.value;setHexInput(v); if(isValidHex(v)){const{h,s,v:bv}=hexToHsv(v);setHue(h);setSat(s);setVal(bv);setHexError(false);} else setHexError(true); }; if(mode==='suggestions') return(
{/* Current preview */}
{value}
{/* Swatches */}
{COLOUR_SUGGESTIONS.map(hex=>(
); return(
{setSat(s);setVal(v);}}/>
); } // Bottom-sheet wrapper for mobile — position:fixed, slides up from bottom export default function ColourPickerSheet({ value, onChange, onClose, title='Pick a colour' }) { return (
e.target===e.currentTarget&&onClose()}>
{title}
{onChange(v);}}/>
); }