708 lines
25 KiB
TypeScript
708 lines
25 KiB
TypeScript
import { useState, useEffect, useCallback } from 'react'
|
||
import { FaThLarge } from 'react-icons/fa'
|
||
import {
|
||
parseReactCode,
|
||
updateComponentProp,
|
||
updateComponentProps,
|
||
generateHookCode,
|
||
generateHookVariableName,
|
||
removeHookFromCode,
|
||
generateUniqueId,
|
||
removeComponentAndHooksFromCode,
|
||
generateComponentJSX,
|
||
insertJSXAtPosition,
|
||
} from '../../utils/codeParser'
|
||
import { ComponentLibrary } from '../../components/codeLayout/ComponentLibrary'
|
||
import { Splitter } from '../../components/codeLayout/Splitter'
|
||
import { PanelManager } from '../../components/codeLayout/PanelManager'
|
||
import { CodeEditor } from '../../components/codeLayout/CodeEditor'
|
||
import { ComponentDefinition, ComponentInfo, EditorState } from '../../proxy/developerKit/componentInfo'
|
||
import PropertyPanel from '../../components/codeLayout/PropertyPanel'
|
||
import ComponentSelector from '../../components/codeLayout/ComponentSelector'
|
||
import { useParams } from 'react-router-dom'
|
||
import { useComponents } from '../../contexts/ComponentContext'
|
||
import { toast } from '../../components/ui'
|
||
import Notification from '../../components/ui/Notification/Notification'
|
||
import { PanelState } from '../../components/codeLayout/data/componentDefinitions'
|
||
|
||
const INITIAL_CODE = `const Component = () => {
|
||
return (
|
||
<>
|
||
</>
|
||
);
|
||
};
|
||
|
||
export default Component
|
||
`
|
||
|
||
function CodeLayout() {
|
||
const { id } = useParams()
|
||
const { getComponent, updateComponent } = useComponents()
|
||
const [showPanelManager, setShowPanelManager] = useState(false)
|
||
const [panelState, setPanelState] = useState<PanelState>({
|
||
toolbox: true,
|
||
properties: true,
|
||
})
|
||
const [editorState, setEditorState] = useState<EditorState>({
|
||
code: INITIAL_CODE,
|
||
components: [],
|
||
selectedComponentId: null,
|
||
})
|
||
|
||
const isEditing = !!id
|
||
const [code, setCode] = useState<string>(INITIAL_CODE)
|
||
const [hasCodeChanges, setHasCodeChanges] = useState(false)
|
||
const [isLoaded, setIsLoaded] = useState(false)
|
||
const [name, setName] = useState('')
|
||
const [dependencies, setDependencies] = useState<string[]>([])
|
||
const [isActive, setIsActive] = useState(true)
|
||
|
||
const handleSave = async () => {
|
||
try {
|
||
const componentData = {
|
||
name: name.trim(),
|
||
dependencies: JSON.stringify(dependencies), // Serialize dependencies to JSON string
|
||
code: code.trim(),
|
||
isActive,
|
||
}
|
||
|
||
if (isEditing && id) {
|
||
updateComponent(id, componentData)
|
||
parseAndUpdateComponents(componentData.code)
|
||
}
|
||
} catch (error) {
|
||
console.error('Error saving component:', error)
|
||
alert('Failed to save component. Please try again.')
|
||
} finally {
|
||
setHasCodeChanges(false)
|
||
setIsLoaded(true)
|
||
|
||
toast.push(
|
||
<Notification type="success" duration={2000}>
|
||
"Bileşen başarıyla kaydedildi."
|
||
</Notification>,
|
||
{
|
||
placement: 'top-end',
|
||
},
|
||
)
|
||
}
|
||
}
|
||
|
||
// Load existing component data - sadece edit modunda
|
||
useEffect(() => {
|
||
if (isEditing && id && !isLoaded) {
|
||
const component = getComponent(id)
|
||
if (component) {
|
||
setName(component.name)
|
||
// setDescription(component.description || "");
|
||
// Parse dependencies from JSON string
|
||
try {
|
||
const deps = component.dependencies ? JSON.parse(component.dependencies) : []
|
||
setDependencies(Array.isArray(deps) ? deps : [])
|
||
} catch {
|
||
setDependencies([])
|
||
}
|
||
setCode(component.code) // Mevcut kodu yükle
|
||
// Parse components from the loaded code
|
||
parseAndUpdateComponents(component.code)
|
||
setIsActive(component.isActive)
|
||
setIsLoaded(true)
|
||
}
|
||
} else if (!isEditing && !isLoaded) {
|
||
// Yeni komponent için boş başla - TEMPLATE YOK
|
||
setIsLoaded(true)
|
||
}
|
||
}, [id, isEditing, getComponent, isLoaded])
|
||
|
||
// NEW: Handle drop to Code Editor instead of Canvas
|
||
const handleDropToCodeEditor = (
|
||
componentDef: ComponentDefinition,
|
||
position: { line: number; column: number },
|
||
): void => {
|
||
if (!componentDef || typeof componentDef !== 'object') {
|
||
console.error('Component definition is null or undefined')
|
||
return
|
||
}
|
||
if (!componentDef.name) {
|
||
console.error('Invalid component definition in handleDropToCodeEditor')
|
||
return
|
||
}
|
||
|
||
const propertiesObj: Record<string, any> = {}
|
||
if (Array.isArray(componentDef.properties)) {
|
||
componentDef.properties.forEach((prop) => {
|
||
propertiesObj[prop.name] = prop
|
||
})
|
||
}
|
||
const newComponent: ComponentInfo = {
|
||
id: generateUniqueId(),
|
||
type: componentDef.name, // type alanı componentDef.name olmalı
|
||
props: propertiesObj,
|
||
name: componentDef.name,
|
||
startLine: 0,
|
||
endLine: 0,
|
||
startColumn: 0,
|
||
endColumn: 0,
|
||
}
|
||
|
||
// Generate JSX string for this component
|
||
const componentJSX = generateComponentJSX(newComponent)
|
||
|
||
// Insert the JSX at the cursor position in the code editor
|
||
setCode(insertJSXAtPosition(code, componentJSX, position))
|
||
}
|
||
|
||
const handleAppDragOver = (e: React.DragEvent<HTMLDivElement>) => {
|
||
e.preventDefault()
|
||
}
|
||
|
||
const parseAndUpdateComponents = useCallback((code: string) => {
|
||
try {
|
||
const parsed = parseReactCode(code)
|
||
setEditorState((prev) => ({
|
||
...prev,
|
||
code,
|
||
components: parsed.components,
|
||
}))
|
||
} catch (error) {
|
||
const msg = error instanceof Error ? error.message : 'Koddan komponentler parse edilemedi.'
|
||
console.log('Parse error:', msg)
|
||
setEditorState((prev) => ({
|
||
...prev,
|
||
code, // Only update the code, keep existing components
|
||
}))
|
||
}
|
||
}, [])
|
||
|
||
// Handle code changes from Monaco Editor
|
||
const handleCodeChange = useCallback(
|
||
(value: string | undefined) => {
|
||
if (value !== undefined) {
|
||
setCode(value)
|
||
setHasCodeChanges(value !== editorState.code)
|
||
}
|
||
},
|
||
[editorState.code],
|
||
)
|
||
|
||
// Apply code changes
|
||
const handleApplyCodeChanges = useCallback(() => {
|
||
parseAndUpdateComponents(code)
|
||
setHasCodeChanges(false)
|
||
}, [parseAndUpdateComponents, code])
|
||
|
||
// Reset code changes
|
||
const handleResetCodeChanges = useCallback(() => {
|
||
setCode(editorState.code)
|
||
setHasCodeChanges(false)
|
||
}, [editorState.code])
|
||
|
||
// Handle property changes from Property Panel
|
||
// Handle hook toggle
|
||
const handleHookToggle = useCallback(
|
||
(componentId: string, hookType: string, enabled: boolean) => {
|
||
console.log('🪝 App: handleHookToggle called:', {
|
||
componentId,
|
||
hookType,
|
||
enabled,
|
||
})
|
||
const selectedComponent = editorState.components?.find((c) => c.id === componentId)
|
||
if (!selectedComponent) return
|
||
|
||
// Use the most up-to-date code (pendingCode if available, otherwise editorState.code)
|
||
let updatedCode = code || editorState.code
|
||
console.log('🔍 App: Using code source:', code === editorState.code ? 'same' : 'pendingCode')
|
||
|
||
if (enabled) {
|
||
// Check if hook is already present - more specific check
|
||
const varName = generateHookVariableName(hookType, componentId)
|
||
console.log('🔍 App: Checking for existing hook variable:', varName)
|
||
|
||
// Create a more specific regex to check for actual hook declarations
|
||
const hookDeclarationRegex = new RegExp(
|
||
`const\\s+(?:\\[.*?${varName}.*?\\]|${varName})\\s*=\\s*${hookType}\\s*\\(`,
|
||
)
|
||
if (hookDeclarationRegex.test(updatedCode)) {
|
||
console.log('⚠️ App: Hook already exists, skipping')
|
||
return // Hook already exists
|
||
}
|
||
|
||
// Handle React imports - improved approach
|
||
const reactImportRegex = /^import\s+\{([^}]*)\}\s+from\s+['"]react['"]\s*;?/m
|
||
const reactMatch = reactImportRegex.exec(updatedCode)
|
||
|
||
if (reactMatch) {
|
||
// reactMatch[1] --> süslü parantez içi örn: "useState, useRef"
|
||
let existingHooks = reactMatch[1]
|
||
.split(',')
|
||
.map((h) => h.trim())
|
||
.filter((h) => h)
|
||
|
||
// Eğer hookType yoksa ekle
|
||
if (!existingHooks.includes(hookType)) {
|
||
existingHooks.push(hookType)
|
||
}
|
||
|
||
// Tekrarları kaldır (güvenlik için)
|
||
existingHooks = [...new Set(existingHooks)]
|
||
|
||
const newImport = `import { ${existingHooks.join(', ')} } from 'react';\n`
|
||
updatedCode = updatedCode.replace(reactImportRegex, newImport)
|
||
|
||
console.log('🔄 App: Updated existing React import:', newImport.trim())
|
||
} else {
|
||
// React import satırı yoksa ekle
|
||
const importLine = `import { ${hookType} } from 'react';\n\n`
|
||
updatedCode = importLine + updatedCode
|
||
console.log('🔄 App: Added new React import:', importLine.trim())
|
||
}
|
||
|
||
// Add hook declaration
|
||
const hookCode = generateHookCode(hookType, componentId, selectedComponent.type)
|
||
console.log('🔍 App: Generated hook code:', hookCode)
|
||
|
||
// Try multiple patterns for function declaration
|
||
const functionPatterns = [
|
||
/function\s+Component\s*\([^)]*\)\s*\{/, // function Component() {
|
||
/function\s+\w+\s*\([^)]*\)\s*\{/, // function AnyName() {
|
||
/const\s+\w+\s*=\s*\([^)]*\)\s*=>\s*\{/, // const Component = () => {
|
||
/export\s+default\s+function\s*\([^)]*\)\s*\{/, // export default function() {
|
||
]
|
||
|
||
let match2 = null
|
||
for (const pattern of functionPatterns) {
|
||
match2 = updatedCode.match(pattern)
|
||
if (match2) {
|
||
console.log('🔍 App: Found function with pattern:', pattern)
|
||
break
|
||
}
|
||
}
|
||
|
||
console.log('🔍 App: Function match result:', match2)
|
||
console.log('🔍 App: Updated code preview:', updatedCode.substring(0, 500))
|
||
|
||
if (match2 && typeof match2.index === 'number') {
|
||
const insertPosition = match2.index + match2[0].length
|
||
updatedCode =
|
||
updatedCode.slice(0, insertPosition) +
|
||
'\n ' +
|
||
hookCode +
|
||
'\n' +
|
||
updatedCode.slice(insertPosition)
|
||
console.log('✅ App: Hook code inserted successfully')
|
||
} else {
|
||
console.log('⚠️ App: Could not find function body to insert hook')
|
||
// Fallback: insert after the first opening brace
|
||
const firstBrace = updatedCode.indexOf('{')
|
||
if (firstBrace !== -1) {
|
||
updatedCode =
|
||
updatedCode.slice(0, firstBrace + 1) +
|
||
'\n ' +
|
||
hookCode +
|
||
'\n' +
|
||
updatedCode.slice(firstBrace + 1)
|
||
console.log('✅ App: Hook code inserted using fallback method')
|
||
}
|
||
}
|
||
|
||
// Update component properties if needed
|
||
if (hookType === 'useState') {
|
||
const setterName = `set${varName.charAt(0).toUpperCase() + varName.slice(1)}`
|
||
|
||
// Update component props based on type
|
||
if (selectedComponent.type === 'input') {
|
||
updatedCode = updateComponentProp(updatedCode, componentId, 'value', `{${varName}}`)
|
||
updatedCode = updateComponentProp(
|
||
updatedCode,
|
||
componentId,
|
||
'onChange',
|
||
`{(e) => ${setterName}(e.target.value)}`,
|
||
)
|
||
} else if (selectedComponent.type === 'button') {
|
||
updatedCode = updateComponentProp(
|
||
updatedCode,
|
||
componentId,
|
||
'onClick',
|
||
`{() => ${setterName}(!${varName})}`,
|
||
)
|
||
} else if (selectedComponent.type === 'checkbox') {
|
||
updatedCode = updateComponentProp(updatedCode, componentId, 'checked', `{${varName}}`)
|
||
updatedCode = updateComponentProp(
|
||
updatedCode,
|
||
componentId,
|
||
'onChange',
|
||
`{(val) => ${setterName}(val)}`,
|
||
)
|
||
}
|
||
} else if (hookType === 'useRef') {
|
||
updatedCode = updateComponentProp(updatedCode, componentId, 'ref', `{${varName}}`)
|
||
}
|
||
} else {
|
||
// Remove hook
|
||
updatedCode = removeHookFromCode(updatedCode, hookType, componentId)
|
||
|
||
// Remove related props
|
||
if (hookType === 'useState') {
|
||
if (selectedComponent.type === 'input') {
|
||
updatedCode = updateComponentProp(updatedCode, componentId, 'value', '')
|
||
updatedCode = updateComponentProp(updatedCode, componentId, 'onChange', null)
|
||
} else if (selectedComponent.type === 'button') {
|
||
updatedCode = updateComponentProp(updatedCode, componentId, 'onClick', null)
|
||
} else if (selectedComponent.type === 'checkbox') {
|
||
updatedCode = updateComponentProp(updatedCode, componentId, 'checked', false)
|
||
updatedCode = updateComponentProp(updatedCode, componentId, 'onChange', null)
|
||
}
|
||
} else if (hookType === 'useRef') {
|
||
updatedCode = updateComponentProp(updatedCode, componentId, 'ref', null)
|
||
}
|
||
}
|
||
|
||
setEditorState((prev) => {
|
||
console.log('🔄 App: Final updatedCode before parsing:', updatedCode)
|
||
const parsed = parseReactCode(updatedCode)
|
||
console.log('🔄 App: Parsed components:', parsed.components?.length)
|
||
const newState = {
|
||
code: updatedCode,
|
||
components: parsed.components,
|
||
selectedComponentId: prev.selectedComponentId, // Preserve selection
|
||
}
|
||
console.log('🔄 App: New editor state code preview:', newState.code.substring(0, 300))
|
||
return newState
|
||
})
|
||
|
||
// Also update pending code to match
|
||
setCode(updatedCode)
|
||
},
|
||
[editorState.code, editorState.components, code],
|
||
)
|
||
|
||
// Zincirleme hook güncelleme fonksiyonu
|
||
const applyMultipleHookToggles = (
|
||
toggles: { componentId: string; hookType: string; enabled: boolean }[],
|
||
) => {
|
||
let updatedCode = code || editorState.code
|
||
// 1. Sadece kaldırılması gereken hook'ları kaldır
|
||
toggles
|
||
.filter((t) => t.enabled === false)
|
||
.forEach(({ hookType, componentId }) => {
|
||
const selectedComponent = editorState.components?.find((c) => c.id === componentId)
|
||
if (!selectedComponent) return
|
||
updatedCode = removeHookFromCode(updatedCode, hookType, componentId)
|
||
// Prop temizliği
|
||
if (hookType === 'useState') {
|
||
if (selectedComponent.type === 'input') {
|
||
updatedCode = updateComponentProp(updatedCode, componentId, 'value', '')
|
||
updatedCode = updateComponentProp(updatedCode, componentId, 'onChange', null)
|
||
} else if (selectedComponent.type === 'button') {
|
||
updatedCode = updateComponentProp(updatedCode, componentId, 'onClick', null)
|
||
} else if (selectedComponent.type === 'checkbox') {
|
||
updatedCode = updateComponentProp(updatedCode, componentId, 'checked', false)
|
||
updatedCode = updateComponentProp(updatedCode, componentId, 'onChange', null)
|
||
}
|
||
} else if (hookType === 'useRef') {
|
||
updatedCode = updateComponentProp(updatedCode, componentId, 'ref', null)
|
||
}
|
||
})
|
||
// 2. Eklenmesi gereken hook'ları ekle (veya zaten varsa dokunma)
|
||
toggles
|
||
.filter((t) => t.enabled === true)
|
||
.forEach(({ hookType, componentId }) => {
|
||
const selectedComponent = editorState.components?.find((c) => c.id === componentId)
|
||
if (!selectedComponent) return
|
||
const varName = generateHookVariableName(hookType, componentId)
|
||
// Hook kodu fonksiyon gövdesinde yoksa ekle
|
||
const hookDeclarationRegex = new RegExp(
|
||
`const\\s+(?:\\[.*?${varName}.*?\\]|${varName})\\s*=\\s*${hookType}\\s*\\(`,
|
||
)
|
||
if (!hookDeclarationRegex.test(updatedCode)) {
|
||
const reactImportRegex =
|
||
// eslint-disable-next-line no-useless-escape
|
||
/import\\s+React(?:\\s*,\\s*\\{([^}]*)\\})?\\s+from\\s+['\"]react['\"];?/
|
||
const importMatch = updatedCode.match(reactImportRegex)
|
||
const allHooks = new Set<string>()
|
||
if (importMatch && importMatch[1]) {
|
||
importMatch[1]
|
||
.split(',')
|
||
.map((h) => h.trim())
|
||
.filter((h) => h)
|
||
.forEach((h) => allHooks.add(h))
|
||
}
|
||
allHooks.add(hookType)
|
||
let newImport = ''
|
||
if (allHooks.size > 0) {
|
||
newImport = `import { ${Array.from(allHooks).join(', ')} } from 'react';\n`
|
||
}
|
||
if (importMatch) {
|
||
updatedCode = updatedCode.replace(reactImportRegex, newImport)
|
||
} else {
|
||
updatedCode = newImport + updatedCode
|
||
}
|
||
// Hook kodunu fonksiyon gövdesine ekle
|
||
const hookCode = generateHookCode(hookType, componentId, selectedComponent.type)
|
||
const functionPatterns = [
|
||
/function\s+Component\s*\([^)]*\)\s*\{/, // function Component() {
|
||
/function\s+\w+\s*\([^)]*\)\s*\{/, // function AnyName() {
|
||
/const\s+\w+\s*=\s*\([^)]*\)\s*=>\s*\{/, // const Component = () => {
|
||
/export\s+default\s+function\s*\([^)]*\)\s*\{/, // export default function() {
|
||
]
|
||
let match2 = null
|
||
for (const pattern of functionPatterns) {
|
||
match2 = updatedCode.match(pattern)
|
||
if (match2) break
|
||
}
|
||
if (match2 && typeof match2.index === 'number') {
|
||
const insertPosition = match2.index + match2[0].length
|
||
updatedCode =
|
||
updatedCode.slice(0, insertPosition) +
|
||
'\n ' +
|
||
hookCode +
|
||
'\n' +
|
||
updatedCode.slice(insertPosition)
|
||
} else {
|
||
const firstBrace = updatedCode.indexOf('{')
|
||
if (firstBrace !== -1) {
|
||
updatedCode =
|
||
updatedCode.slice(0, firstBrace + 1) +
|
||
'\n ' +
|
||
hookCode +
|
||
'\n' +
|
||
updatedCode.slice(firstBrace + 1)
|
||
}
|
||
}
|
||
}
|
||
// Prop güncellemeleri
|
||
if (hookType === 'useState') {
|
||
const setterName = `set${varName.charAt(0).toUpperCase() + varName.slice(1)}`
|
||
if (selectedComponent.type === 'input') {
|
||
updatedCode = updateComponentProp(updatedCode, componentId, 'value', `{${varName}}`)
|
||
updatedCode = updateComponentProp(
|
||
updatedCode,
|
||
componentId,
|
||
'onChange',
|
||
`{(e) => ${setterName}(e.target.value)}`,
|
||
)
|
||
} else if (selectedComponent.type === 'button') {
|
||
updatedCode = updateComponentProp(
|
||
updatedCode,
|
||
componentId,
|
||
'onClick',
|
||
`{() => ${setterName}(!${varName})}`,
|
||
)
|
||
} else if (selectedComponent.type === 'checkbox') {
|
||
updatedCode = updateComponentProp(updatedCode, componentId, 'checked', `{${varName}}`)
|
||
updatedCode = updateComponentProp(
|
||
updatedCode,
|
||
componentId,
|
||
'onChange',
|
||
`{(e) => ${setterName}(e.target.checked)}`,
|
||
)
|
||
}
|
||
} else if (hookType === 'useRef') {
|
||
updatedCode = updateComponentProp(updatedCode, componentId, 'ref', `{${varName}}`)
|
||
}
|
||
})
|
||
setEditorState((prev) => {
|
||
const parsed = parseReactCode(updatedCode)
|
||
return {
|
||
code: updatedCode,
|
||
components: parsed.components,
|
||
selectedComponentId: prev.selectedComponentId,
|
||
}
|
||
})
|
||
setCode(updatedCode)
|
||
}
|
||
|
||
// Handle multiple property changes at once
|
||
const handlePropertiesChange = useCallback(
|
||
(componentId: string, updates: Record<string, any>) => {
|
||
console.log('🔄 App: handlePropertiesChange called:', {
|
||
componentId,
|
||
updates,
|
||
})
|
||
|
||
const updatedCode = updateComponentProps(editorState.code, componentId, updates)
|
||
console.log('📝 App: Properties updated, code changed:', editorState.code !== updatedCode)
|
||
|
||
setEditorState((prev) => {
|
||
const parsed = parseReactCode(updatedCode)
|
||
return {
|
||
code: updatedCode,
|
||
components: parsed.components,
|
||
selectedComponentId: prev.selectedComponentId, // Preserve selection
|
||
}
|
||
})
|
||
|
||
// Update pending code to reflect changes
|
||
setCode(updatedCode)
|
||
},
|
||
[editorState.code],
|
||
)
|
||
|
||
// Handle component list refresh
|
||
const handleRefreshComponents = useCallback(() => {
|
||
parseAndUpdateComponents(editorState.code)
|
||
}, [parseAndUpdateComponents, editorState.code])
|
||
|
||
// Handle component selection
|
||
const handleSelectComponent = useCallback((componentId: string | null) => {
|
||
setEditorState((prev) => ({
|
||
...prev,
|
||
selectedComponentId: componentId,
|
||
}))
|
||
}, [])
|
||
|
||
// Initialize parsing on mount
|
||
useEffect(() => {
|
||
parseAndUpdateComponents(INITIAL_CODE)
|
||
}, [parseAndUpdateComponents])
|
||
|
||
const handleDragStart = (_componentDef: ComponentDefinition, e: React.DragEvent) => {
|
||
e.stopPropagation()
|
||
}
|
||
|
||
const selectedComponent =
|
||
editorState.components?.find((c) => c.id === editorState.selectedComponentId) || null
|
||
|
||
const renderLeftPanel = () => {
|
||
if (!panelState.toolbox) return null
|
||
return <ComponentLibrary onDragStart={handleDragStart} />
|
||
}
|
||
|
||
// Komponent ve ilgili hook'ları koddan silen fonksiyon
|
||
const handleDeleteComponent = (componentId: string) => {
|
||
// Koddan JSX ve hook'ları sil
|
||
const updatedCode = removeComponentAndHooksFromCode(code, componentId)
|
||
// Koddan parse edip state'i güncelle
|
||
parseAndUpdateComponents(updatedCode)
|
||
setCode(updatedCode)
|
||
// Seçili komponenti kaldır
|
||
setEditorState((prev) => ({
|
||
...prev,
|
||
selectedComponentId: null,
|
||
}))
|
||
}
|
||
|
||
const renderRightPanel = () => {
|
||
if (!panelState.properties) return null
|
||
|
||
return (
|
||
<div className="flex flex-col flex-1 min-h-0 h-full w-full">
|
||
<ComponentSelector
|
||
components={editorState.components}
|
||
selectedComponentId={editorState.selectedComponentId}
|
||
onSelectComponent={handleSelectComponent}
|
||
onRefresh={handleRefreshComponents}
|
||
/>
|
||
|
||
<PropertyPanel
|
||
selectedComponent={selectedComponent}
|
||
currentCode={editorState.code}
|
||
onPropertiesChange={handlePropertiesChange}
|
||
onHookToggle={handleHookToggle}
|
||
onMultipleHookToggle={applyMultipleHookToggles}
|
||
onDeleteComponent={handleDeleteComponent}
|
||
/>
|
||
</div>
|
||
)
|
||
}
|
||
|
||
const mainContent = (
|
||
<div className="flex-1 flex flex-col min-h-0 h-full">
|
||
{/* Top Header */}
|
||
<div className="bg-white border-b border-gray-200 px-3 py-3">
|
||
<div className="flex items-center justify-between">
|
||
<div className="flex items-center space-x-4">
|
||
<h1 className="text-lg font-semibold text-gray-900">{name}</h1>
|
||
<p className="text-xs text-gray-500">{dependencies.join(', ')}</p>
|
||
</div>
|
||
|
||
<div className="flex items-center space-x-3">
|
||
<button
|
||
onClick={() => setShowPanelManager(true)}
|
||
className="flex items-center space-x-2 px-3 py-2 bg-white border border-gray-300 rounded-lg hover:bg-gray-50 transition-colors"
|
||
title="Panel Manager"
|
||
>
|
||
<FaThLarge className="w-4 h-4 text-gray-600" />
|
||
<span className="text-sm text-gray-600">Panels</span>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="flex flex-col flex-1 min-h-0 h-full">
|
||
<div className="flex-1 h-full min-h-0">
|
||
<CodeEditor
|
||
code={code}
|
||
onChange={handleCodeChange}
|
||
onApplyCodeChanges={handleApplyCodeChanges}
|
||
onResetCodeChanges={handleResetCodeChanges}
|
||
onDrop={handleDropToCodeEditor}
|
||
onComponentSave={handleSave}
|
||
/>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
)
|
||
|
||
return (
|
||
<div
|
||
className="flex h-screen bg-gray-50"
|
||
onDragOver={handleAppDragOver}
|
||
onDrop={(e) => {
|
||
e.preventDefault()
|
||
e.stopPropagation()
|
||
}}
|
||
>
|
||
{/* Panel Yöneticisi Modal */}
|
||
{showPanelManager && (
|
||
<PanelManager
|
||
isOpen={showPanelManager}
|
||
onClose={() => setShowPanelManager(false)}
|
||
panelState={panelState}
|
||
onPanelToggle={(panel) => setPanelState((prev) => ({ ...prev, [panel]: !prev[panel] }))}
|
||
/>
|
||
)}
|
||
{/* Sol Sidebar ve ana içerik */}
|
||
{panelState.toolbox ? (
|
||
<Splitter direction="horizontal" initialSize={288} minSize={200} maxSize={400}>
|
||
{renderLeftPanel()}
|
||
<div className="flex flex-1">
|
||
{panelState.properties ? (
|
||
<Splitter
|
||
direction="horizontal"
|
||
initialSize={400}
|
||
minSize={300}
|
||
maxSize={window.innerWidth - 288 - 100}
|
||
reverse={true}
|
||
>
|
||
{mainContent}
|
||
{renderRightPanel()}
|
||
</Splitter>
|
||
) : (
|
||
mainContent
|
||
)}
|
||
</div>
|
||
</Splitter>
|
||
) : (
|
||
<div className="flex flex-1">
|
||
{panelState.properties ? (
|
||
<Splitter
|
||
direction="horizontal"
|
||
initialSize={400}
|
||
minSize={300}
|
||
maxSize={window.innerWidth - 100}
|
||
reverse={true}
|
||
>
|
||
{mainContent}
|
||
{renderRightPanel()}
|
||
</Splitter>
|
||
) : (
|
||
mainContent
|
||
)}
|
||
</div>
|
||
)}
|
||
</div>
|
||
)
|
||
}
|
||
|
||
export default CodeLayout
|