import { useState, useEffect } from "react"; import TailwindModal from "./TailwindModal"; import { ComponentInfo, HookInfo, PropertyInfo } from "../../proxy/developerKit/componentInfo"; import { getComponentDefinition } from "./data/componentDefinitions"; import { Button } from "../ui"; interface PropertyPanelProps { selectedComponent: ComponentInfo | null; currentCode: string; onPropertiesChange: ( componentId: string, updates: Record ) => void; onHookToggle: ( componentId: string, hookType: string, enabled: boolean ) => void; onMultipleHookToggle: ( toggles: { componentId: string; hookType: string; enabled: boolean }[] ) => void; onDeleteComponent: (componentId: string) => void; } const PropertyPanel: React.FC = ({ selectedComponent, currentCode, onPropertiesChange, onHookToggle, onMultipleHookToggle, onDeleteComponent, }) => { const [tailwindModalOpen, setTailwindModalOpen] = useState(false); const [currentTailwindProperty, setCurrentTailwindProperty] = useState(""); const [activeHooks, setActiveHooks] = useState>(new Set()); const [activeTab, setActiveTab] = useState<"props" | "hooks">("props"); // Local state for pending changes const [pendingProperties, setPendingProperties] = useState< Record >({}); const [pendingEvents, setPendingEvents] = useState>( {} ); const [pendingHooks, setPendingHooks] = useState>({}); const [hasChanges, setHasChanges] = useState(false); const [hasHookChanges, setHasHookChanges] = useState(false); const componentDefinition = selectedComponent ? getComponentDefinition(selectedComponent.name) : null; // Reset pending changes when component changes useEffect(() => { setPendingProperties({}); setPendingEvents({}); setPendingHooks({}); setHasChanges(false); setHasHookChanges(false); }, [selectedComponent?.id]); // Check which hooks are currently active in the code useEffect(() => { if (selectedComponent && currentCode) { const hooks = new Set(); // Check for useState if ( currentCode.includes( `const [state_${selectedComponent.id}, setState_${selectedComponent.id}]` ) ) { hooks.add("useState"); } // Check for useRef if ( currentCode.includes(`const ref_${selectedComponent.id}`) && currentCode.includes(`ref={ref_${selectedComponent.id}`) ) { hooks.add("useRef"); } // // Check for useEffect // if ( // currentCode.includes("useEffect") && // currentCode.includes(selectedComponent.id) // ) { // hooks.add("useEffect"); // } setActiveHooks(hooks); } }, [selectedComponent, currentCode]); // Handle local property changes const handleLocalPropertyChange = (propName: string, value: any) => { setPendingProperties((prev) => ({ ...prev, [propName]: value, })); setHasChanges(true); }; // Handle local hook changes const handleLocalHookToggle = (hookType: string, enabled: boolean) => { setPendingHooks((prev) => ({ ...prev, [hookType]: enabled, })); setHasHookChanges(true); }; // Handle local event changes const handleLocalEventChange = (eventName: string, value: string) => { setPendingEvents((prev) => ({ ...prev, [eventName]: value, })); setHasChanges(true); }; // Apply only property/event changes const handleApplyPropChanges = () => { if (!selectedComponent) return; // Combine all changes into a single update object const allUpdates = { ...pendingProperties, ...pendingEvents, }; // Apply property and event changes together if (Object.keys(allUpdates).length > 0) { onPropertiesChange(selectedComponent.id, allUpdates); } // Reset pending changes setPendingProperties({}); setPendingEvents({}); setHasChanges(false); }; // Apply only hook changes const handleApplyHookChanges = () => { if (!selectedComponent) return; const hookToggles = Object.entries(pendingHooks).map( ([hookType, enabled]) => ({ componentId: selectedComponent.id, hookType, enabled, }) ); if (hookToggles.length > 1) { onMultipleHookToggle(hookToggles); } else if (hookToggles.length === 1) { const { componentId, hookType, enabled } = hookToggles[0]; onHookToggle(componentId, hookType, enabled); } // Reset pending changes setPendingHooks({}); setHasHookChanges(false); }; // Reset all pending changes const handleResetChanges = () => { setPendingProperties({}); setPendingEvents({}); setPendingHooks({}); setHasChanges(false); setHasHookChanges(false); }; const openTailwindModal = (propertyName: string) => { setCurrentTailwindProperty(propertyName); setTailwindModalOpen(true); }; const handleTailwindClassSelect = (className: string) => { const currentValue = pendingProperties[currentTailwindProperty] || selectedComponent?.props[currentTailwindProperty] || ""; const newValue = currentValue ? `${currentValue} ${className}` : className; handleLocalPropertyChange(currentTailwindProperty, newValue); // Don't close modal - let user continue selecting }; const renderPropertyControl = (property: PropertyInfo) => { // Handle children property specially - get from children, not props const currentValue = property.name === "children" ? typeof selectedComponent?.children === "string" ? selectedComponent.children : selectedComponent?.props[property.name] || property.value : selectedComponent?.props[property.name] || property.value; // Use pending value if available, otherwise use current value const value = pendingProperties[property.name] !== undefined ? pendingProperties[property.name] : currentValue; const isTailwindProperty = ["className", "class", "css"].includes( property.name ); const isColorProperty = property.name.toLowerCase().includes("color"); // Don't show children property if component has nested elements if ( property.name === "children" && Array.isArray(selectedComponent?.children) ) { return null; } let arrayError = ""; let arrayInputValue = ""; if (property.type === "array") { try { arrayInputValue = JSON.stringify(value, null, 2); } catch (e) { arrayInputValue = ""; arrayError = "Array verisi gösterilemiyor."; } } return (
{property.type === "boolean" && ( )} {property.type === "select" && property.options && ( )} {property.type === "function" && ( <> handleLocalPropertyChange(property.name, e.target.value) } className="flex-1 px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 dark:bg-gray-800 dark:text-white dark:placeholder-gray-400" placeholder={`Enter ${property.name}`} /> {isTailwindProperty && ( )} {isColorProperty && ( handleLocalPropertyChange(property.name, e.target.value) } className="w-10 h-10 border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-800" /> )} )} {property.type === "string" && ( <> handleLocalPropertyChange(property.name, e.target.value) } className="flex-1 px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 dark:bg-gray-800 dark:text-white dark:placeholder-gray-400" placeholder={`Enter ${property.name}`} /> {isTailwindProperty && ( )} {isColorProperty && ( handleLocalPropertyChange(property.name, e.target.value) } className="w-10 h-10 border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-800" /> )} )} {property.type === "number" && ( handleLocalPropertyChange(property.name, Number(e.target.value)) } className="flex-1 px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 dark:bg-gray-800 dark:text-white dark:placeholder-gray-400" /> )} {property.type === "array" && ( <>