diff --git a/client/public/locales/en/common.json b/client/public/locales/en/common.json index da63a8b..f11c774 100644 --- a/client/public/locales/en/common.json +++ b/client/public/locales/en/common.json @@ -52,6 +52,11 @@ "validationError": "Validation error: {{error}}" }, "kofi": "Tip me on Ko-fi", + "color": { + "click_to_copy": "Click to copy", + "copied": "Copied {{color}} to clipboard", + "copy_failed": "Failed to copy to clipboard" + }, "loading": "Loading", "version": "Version", "unknown": "Unknown", diff --git a/client/src/components/spoolIcon.tsx b/client/src/components/spoolIcon.tsx index fb09721..e4c0e5d 100644 --- a/client/src/components/spoolIcon.tsx +++ b/client/src/components/spoolIcon.tsx @@ -1,17 +1,22 @@ +import { CopyOutlined } from "@ant-design/icons"; +import { message, Tooltip } from "antd"; +import { useTranslate } from "@refinedev/core"; import "./spoolIcon.css"; interface Props { color: string | { colors: string[]; vertical: boolean }; size?: "small" | "large"; - no_margin? : boolean + no_margin?: boolean; + showHex?: boolean; } export default function SpoolIcon(props: Readonly) { + const t = useTranslate(); let dirClass = "vertical"; - let cols = []; - let size = props.size ? props.size : "small"; - let no_margin = props.no_margin ? "no-margin" : ""; - + let cols: string[] = []; + const size = props.size ? props.size : "small"; + const no_margin = props.no_margin ? "no-margin" : ""; + if (typeof props.color === "string") { cols = [props.color]; } else { @@ -19,16 +24,49 @@ export default function SpoolIcon(props: Readonly) { cols = props.color.colors; } - return ( + // Normalize hex codes (remove # if present, uppercase) + const normalizedCols = cols.map((col) => col.replace("#", "").toUpperCase()); + const hexDisplay = normalizedCols.length === 1 ? `#${normalizedCols[0]}` : normalizedCols.map((c) => `#${c}`).join(", "); + + const copyToClipboard = async () => { + try { + await navigator.clipboard.writeText(hexDisplay); + message.success(t("color.copied", { color: hexDisplay })); + } catch { + message.error(t("color.copy_failed")); + } + }; + + const swatchContent = (
- {cols.map((col) => ( + {normalizedCols.map((col) => (
))}
); + + if (props.showHex) { + return ( + +
+ {swatchContent} + + {hexDisplay} + + +
+
+ ); + } + + return swatchContent; } diff --git a/client/src/pages/filaments/show.tsx b/client/src/pages/filaments/show.tsx index ace7e84..673f621 100644 --- a/client/src/pages/filaments/show.tsx +++ b/client/src/pages/filaments/show.tsx @@ -88,8 +88,7 @@ export const FilamentShow: React.FC = () => { {t("filament.fields.name")} {t("filament.fields.color_hex")} - {colorObj && } - {record?.color_hex && } + {colorObj && } {t("filament.fields.material")} {t("filament.fields.price")} diff --git a/client/src/pages/spools/show.tsx b/client/src/pages/spools/show.tsx index a62c80e..9e0093d 100644 --- a/client/src/pages/spools/show.tsx +++ b/client/src/pages/spools/show.tsx @@ -163,7 +163,7 @@ export const SpoolShow: React.FC = () => { {t("spool.fields.id")} {t("spool.fields.filament")} - {colorObj && } + {colorObj && } {t("spool.fields.price")}