Parcourir la source

feat: add color swatches to selection buttons and centralize color utility

unknown il y a 2 mois
Parent
commit
976394b091
3 fichiers modifiés avec 50 ajouts et 31 suppressions
  1. 7 1
      src/components/ModelUploadSection.vue
  2. 5 30
      src/components/StlViewer.vue
  3. 38 0
      src/lib/colors.ts

+ 7 - 1
src/components/ModelUploadSection.vue

@@ -138,12 +138,17 @@
                 type="button"
                 @click="selectedColor = color"
                 :class="[
-                  'px-6 py-2.5 rounded-2xl border text-xs font-bold transition-all duration-300 capitalize',
+                  'px-4 py-2.5 rounded-2xl border text-xs font-bold transition-all duration-300 capitalize flex items-center gap-2',
                   selectedColor === color
                     ? 'bg-primary text-primary-foreground border-primary shadow-glow scale-105'
                     : 'bg-white border-black/[0.05] text-foreground/60 hover:border-primary/30 hover:text-primary'
                 ]"
               >
+                <div 
+                  class="w-3.5 h-3.5 rounded-full border border-black/10 shrink-0" 
+                  :style="{ backgroundColor: getHexColor(color) }"
+                  :class="{ 'border-white/20': selectedColor === color && !isLightColor(getHexColor(color)) }"
+                />
                 {{ color }}
               </button>
             </div>
@@ -341,6 +346,7 @@ import Button from "./ui/button.vue";
 import { defineAsyncComponent } from "vue";
 const StlViewer = defineAsyncComponent(() => import("@/components/StlViewer.vue"));
 import { submitOrder, getCurrentUser, getMaterials, getPriceEstimate, uploadFilesToServer } from "@/lib/api";
+import { getHexColor, isLightColor } from "@/lib/colors";
 
 interface UploadedFile { id: string; dbId?: number; name: string; size: number; type: string; file?: File; quantity: number; basePrice?: number; isUploading?: boolean; printTime?: string; filamentG?: number; dimensions?: string; }
 

+ 5 - 30
src/components/StlViewer.vue

@@ -7,6 +7,7 @@ import { ref, onMounted, onUnmounted, watch, computed } from "vue";
 import * as THREE from "three";
 import { STLLoader } from "three/addons/loaders/STLLoader.js";
 import { OrbitControls } from "three/addons/controls/OrbitControls.js";
+import { getHexColor } from "@/lib/colors";
 
 interface Props {
   file: File;
@@ -19,44 +20,19 @@ let animationId = 0;
 let renderer: THREE.WebGLRenderer | null = null;
 let mesh: THREE.Mesh | null = null;
 
-// Standard color map for common names
-const COLOR_MAP: Record<string, string> = {
-  "white": "#ffffff",
-  "black": "#1a1a1a",
-  "grey": "#808080",
-  "gray": "#808080",
-  "red": "#ef4444",
-  "blue": "#3b82f6",
-  "green": "#22c55e",
-  "yellow": "#eab308",
-  "orange": "#f97316",
-  "purple": "#a855f7",
-  "pink": "#ec4899",
-  "silver": "#c0c0c0",
-  "gold": "#ffd700",
-  "transparent": "#f3f4f6", // fallback for visual
-  "natural": "#f5f5dc",
-};
-
-const getHexColor = (colorName?: string) => {
-  if (!colorName) return "#808080";
-  if (colorName.startsWith("#")) return colorName;
-  return COLOR_MAP[colorName.toLowerCase()] || "#808080";
-};
-
 const isLight = (hex: string) => {
   const color = hex.startsWith("#") ? hex : getHexColor(hex);
-  const rgb = parseInt(color.slice(1), 16);
+  const rgb = parseInt(color.slice(1, 7), 16);
   const r = (rgb >> 16) & 0xff;
   const g = (rgb >> 8) & 0xff;
   const b = (rgb >> 0) & 0xff;
   const luma = 0.2126 * r + 0.7152 * g + 0.0722 * b;
-  return luma > 150; // Threshold for "light"
+  return luma > 150;
 };
 
 const backgroundColor = computed(() => {
   if (!props.color) return "transparent";
-  return isLight(props.color) ? "#0f172a" : "#f8fafc"; // Slate-900 or Slate-50
+  return isLight(props.color) ? "#0f172a" : "#f8fafc";
 });
 
 watch(() => props.color, (newColor) => {
@@ -79,7 +55,7 @@ onMounted(() => {
 
   renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
   renderer.setSize(width, height);
-  renderer.setClearColor(0x000000, 0); // Always transparent, handled by CSS
+  renderer.setClearColor(0x000000, 0);
   container.value.appendChild(renderer.domElement);
 
   const controls = new OrbitControls(camera, renderer.domElement);
@@ -87,7 +63,6 @@ onMounted(() => {
   controls.autoRotate = true;
   controls.autoRotateSpeed = 3.0;
 
-  // Add lights
   const hemiLight = new THREE.HemisphereLight(0xffffff, 0x444444, 1.2);
   hemiLight.position.set(0, 200, 0);
   scene.add(hemiLight);

+ 38 - 0
src/lib/colors.ts

@@ -0,0 +1,38 @@
+export const COLOR_MAP: Record<string, string> = {
+  "white": "#ffffff",
+  "black": "#1a1a1a",
+  "grey": "#808080",
+  "gray": "#808080",
+  "red": "#ef4444",
+  "blue": "#3b82f6",
+  "green": "#22c55e",
+  "yellow": "#eab308",
+  "orange": "#f97316",
+  "purple": "#a855f7",
+  "pink": "#ec4899",
+  "silver": "#c0c0c0",
+  "gold": "#ffd700",
+  "transparent": "#f3f4f6",
+  "natural": "#f5f5dc",
+  "haki": "#4b5320",
+  "olive": "#808000",
+  "transparent_red": "#ef444488",
+  "transparent_blue": "#3b82f688",
+};
+
+export const getHexColor = (colorName?: string) => {
+  if (!colorName) return "#808080";
+  if (colorName.startsWith("#")) return colorName;
+  const normalized = colorName.toLowerCase().replace(/\s+/g, '_');
+  return COLOR_MAP[normalized] || "#808080";
+};
+
+export const isLightColor = (hex: string) => {
+  const color = hex.startsWith("#") ? hex : getHexColor(hex);
+  const rgb = parseInt(color.slice(1, 7), 16);
+  const r = (rgb >> 16) & 0xff;
+  const g = (rgb >> 8) & 0xff;
+  const b = (rgb >> 0) & 0xff;
+  const luma = 0.2126 * r + 0.7152 * g + 0.0722 * b;
+  return luma > 200; // Threshold for very light colors
+};