Merge branch 'multicolor'
This commit is contained in:
@@ -7,13 +7,12 @@ import { NumberFieldUnit, NumberFieldUnitRange } from "./numberField";
|
||||
import dayjs from "dayjs";
|
||||
import utc from "dayjs/plugin/utc";
|
||||
import { DateField, TextField } from "@refinedev/antd";
|
||||
import Icon from "@ant-design/icons";
|
||||
import SpoolIcon from "../icon_spool.svg?react";
|
||||
import { useTranslate } from "@refinedev/core";
|
||||
import { enrichText } from "../utils/parsing";
|
||||
import { UseQueryResult } from "@tanstack/react-query";
|
||||
import { Link } from "react-router-dom";
|
||||
import { Field, FieldType } from "../utils/queryFields";
|
||||
import SpoolIcon from "./spoolIcon";
|
||||
|
||||
dayjs.extend(utc);
|
||||
|
||||
@@ -296,7 +295,7 @@ export function ActionsColumn<Obj extends Entity>(actionsFn: (record: Obj) => Ac
|
||||
}
|
||||
|
||||
interface SpoolIconColumnProps<Obj extends Entity> extends FilteredQueryColumnProps<Obj> {
|
||||
color: (record: Obj) => string | undefined;
|
||||
color: (record: Obj) => string | { colors: string[]; vertical: boolean } | undefined;
|
||||
}
|
||||
|
||||
export function SpoolIconColumn<Obj extends Entity>(props: SpoolIconColumnProps<Obj>) {
|
||||
@@ -343,19 +342,12 @@ export function SpoolIconColumn<Obj extends Entity>(props: SpoolIconColumnProps<
|
||||
},
|
||||
render: (rawValue, record: Obj) => {
|
||||
const value = props.transform ? props.transform(rawValue) : rawValue;
|
||||
const colorStr = props.color(record);
|
||||
const colorObj = props.color(record);
|
||||
return (
|
||||
<Row wrap={false} justify="space-around" align="middle">
|
||||
{colorStr && (
|
||||
{colorObj && (
|
||||
<Col flex="none">
|
||||
<Icon
|
||||
component={SpoolIcon}
|
||||
style={{
|
||||
color: "#" + colorStr,
|
||||
fontSize: 42,
|
||||
marginRight: 0,
|
||||
}}
|
||||
/>
|
||||
<SpoolIcon color={colorObj} />
|
||||
</Col>
|
||||
)}
|
||||
<Col flex="auto">{value}</Col>
|
||||
|
||||
85
client/src/components/multiColorPicker.tsx
Normal file
85
client/src/components/multiColorPicker.tsx
Normal file
@@ -0,0 +1,85 @@
|
||||
import { CloseOutlined, PlusOutlined } from "@ant-design/icons";
|
||||
import { Badge, Button, ColorPicker, InputNumber, Space } from "antd";
|
||||
|
||||
function generateRandomColor() {
|
||||
return "000000".replace(/0/g, function () {
|
||||
return (~~(Math.random() * 16)).toString(16);
|
||||
});
|
||||
}
|
||||
|
||||
function generateInitialColors(num: number) {
|
||||
const colors = [];
|
||||
for (let i = 0; i < num; i++) {
|
||||
colors.push(generateRandomColor());
|
||||
}
|
||||
return colors;
|
||||
}
|
||||
|
||||
/**
|
||||
* An Ant Design compatible form input for multiple color pickers
|
||||
* The value is a comma separated list of hex values, without hashtags
|
||||
* @param props
|
||||
* @returns
|
||||
*/
|
||||
export function MultiColorPicker(props: {
|
||||
value?: string | null | undefined;
|
||||
onChange?: (value: string | null | undefined) => void;
|
||||
min?: number;
|
||||
max?: number;
|
||||
}) {
|
||||
const values = props.value ? props.value.split(",") : generateInitialColors(props.min ?? 0);
|
||||
if (!props.value && props.onChange) {
|
||||
// Update value immediately
|
||||
props.onChange(values.join(","));
|
||||
}
|
||||
const pickers = values.map((value, idx) => (
|
||||
<Badge
|
||||
key={idx}
|
||||
count={
|
||||
values.length > (props.min ?? 0) ? (
|
||||
<span className="ant-badge-count">
|
||||
<CloseOutlined
|
||||
onClick={() => {
|
||||
// Remove this picker
|
||||
if (props.onChange) {
|
||||
props.onChange(values.filter((v, i) => i !== idx).join(","));
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</span>
|
||||
) : (
|
||||
<></>
|
||||
)
|
||||
}
|
||||
>
|
||||
<ColorPicker
|
||||
format="hex"
|
||||
value={value}
|
||||
onChange={(_, newHex) => {
|
||||
if (props.onChange) {
|
||||
newHex = newHex.replace("#", "");
|
||||
props.onChange(values.map((v, i) => (i === idx ? newHex : v)).join(","));
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</Badge>
|
||||
));
|
||||
|
||||
return (
|
||||
<>
|
||||
<Space direction="horizontal" size="middle" style={{ marginTop: "1em" }}>
|
||||
{pickers}
|
||||
{values.length < (props.max ?? Infinity) && (
|
||||
<Button
|
||||
icon={<PlusOutlined />}
|
||||
onClick={() => {
|
||||
if (props.onChange) {
|
||||
props.onChange(values.concat(generateRandomColor()).join(","));
|
||||
}
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</Space>
|
||||
</>
|
||||
);
|
||||
}
|
||||
41
client/src/components/spoolIcon.css
Normal file
41
client/src/components/spoolIcon.css
Normal file
@@ -0,0 +1,41 @@
|
||||
.spool-icon {
|
||||
display: flex;
|
||||
width: 1.5em;
|
||||
height: 2.5em;
|
||||
gap: 2px;
|
||||
margin: 0 0.5em;
|
||||
}
|
||||
|
||||
.spool-icon.vertical {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.spool-icon.large {
|
||||
width: 4em;
|
||||
height: 4em;
|
||||
}
|
||||
|
||||
.spool-icon * {
|
||||
flex: 1 1 0px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.spool-icon.vertical *:first-child {
|
||||
border-top-left-radius: 6px;
|
||||
border-top-right-radius: 6px;
|
||||
}
|
||||
|
||||
.spool-icon.vertical *:last-child {
|
||||
border-bottom-left-radius: 6px;
|
||||
border-bottom-right-radius: 6px;
|
||||
}
|
||||
|
||||
.spool-icon.horizontal *:first-child {
|
||||
border-top-left-radius: 6px;
|
||||
border-bottom-left-radius: 6px;
|
||||
}
|
||||
|
||||
.spool-icon.horizontal *:last-child {
|
||||
border-top-right-radius: 6px;
|
||||
border-bottom-right-radius: 6px;
|
||||
}
|
||||
32
client/src/components/spoolIcon.tsx
Normal file
32
client/src/components/spoolIcon.tsx
Normal file
@@ -0,0 +1,32 @@
|
||||
import "./spoolIcon.css";
|
||||
|
||||
interface Props {
|
||||
color: string | { colors: string[]; vertical: boolean };
|
||||
size?: "small" | "large";
|
||||
}
|
||||
|
||||
export default function SpoolIcon(props: Props) {
|
||||
let dirClass = "vertical";
|
||||
let cols = [];
|
||||
let size = props.size ? props.size : "small";
|
||||
|
||||
if (typeof props.color === "string") {
|
||||
cols = [props.color];
|
||||
} else {
|
||||
dirClass = props.color.vertical ? "vertical" : "horizontal";
|
||||
cols = props.color.colors;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={"spool-icon " + dirClass + " " + size}>
|
||||
{cols.map((col) => (
|
||||
<div
|
||||
key={col}
|
||||
style={{
|
||||
backgroundColor: "#" + col.replace("#", ""),
|
||||
}}
|
||||
></div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user