multiColorPicker.tsx
1 import { CloseOutlined, PlusOutlined } from "@ant-design/icons"; 2 import { Badge, Button, ColorPicker, Space } from "antd"; 3 4 function generateRandomColor() { 5 return "000000".replace(/0/g, function () { 6 return (~~(Math.random() * 16)).toString(16); 7 }); 8 } 9 10 function generateInitialColors(num: number) { 11 const colors = []; 12 for (let i = 0; i < num; i++) { 13 colors.push(generateRandomColor()); 14 } 15 return colors; 16 } 17 18 /** 19 * An Ant Design compatible form input for multiple color pickers 20 * The value is a comma separated list of hex values, without hashtags 21 * @param props 22 * @returns 23 */ 24 export function MultiColorPicker(props: { 25 value?: string | null | undefined; 26 onChange?: (value: string | null | undefined) => void; 27 min?: number; 28 max?: number; 29 }) { 30 const values = props.value ? props.value.split(",") : generateInitialColors(props.min ?? 0); 31 if (!props.value && props.onChange) { 32 // Update value immediately 33 props.onChange(values.join(",")); 34 } 35 const pickers = values.map((value, idx) => ( 36 <Badge 37 key={idx} 38 count={ 39 values.length > (props.min ?? 0) ? ( 40 <span className="ant-badge-count"> 41 <CloseOutlined 42 onClick={() => { 43 // Remove this picker 44 if (props.onChange) { 45 props.onChange(values.filter((v, i) => i !== idx).join(",")); 46 } 47 }} 48 /> 49 </span> 50 ) : ( 51 <></> 52 ) 53 } 54 > 55 <ColorPicker 56 value={value} 57 onChange={(clr) => { 58 if (props.onChange) { 59 props.onChange(values.map((v, i) => (i === idx ? clr.toHex() : v)).join(",")); 60 } 61 }} 62 /> 63 </Badge> 64 )); 65 66 return ( 67 <> 68 <Space direction="horizontal" size="middle" style={{ marginTop: "1em" }}> 69 {pickers} 70 {values.length < (props.max ?? Infinity) && ( 71 <Button 72 icon={<PlusOutlined />} 73 onClick={() => { 74 if (props.onChange) { 75 props.onChange(values.concat(generateRandomColor()).join(",")); 76 } 77 }} 78 /> 79 )} 80 </Space> 81 </> 82 ); 83 }