/ client / src / components / multiColorPicker.tsx
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  }