207 lines
6.2 KiB
TypeScript
207 lines
6.2 KiB
TypeScript
import {
|
||
Text,
|
||
Shapes,
|
||
Eye,
|
||
EyeSlash,
|
||
Copy,
|
||
Trash,
|
||
Gallery,
|
||
Link2,
|
||
VideoSquare,
|
||
DocumentText,
|
||
ArrowLeft,
|
||
Minus,
|
||
Grid8,
|
||
Layer,
|
||
MouseSquare,
|
||
Sticker,
|
||
} from 'iconsax-react'
|
||
import { useState } from 'react'
|
||
import { useEditorStore } from '../store/editorStore'
|
||
import { clx } from '@/helpers/utils'
|
||
|
||
const LayersList = () => {
|
||
const {
|
||
objects,
|
||
selectedObjectId,
|
||
setSelectedObjectId,
|
||
deleteObject,
|
||
duplicateObject,
|
||
toggleObjectVisibility,
|
||
reorderObjects,
|
||
} = useEditorStore()
|
||
const [draggedObjectId, setDraggedObjectId] = useState<string | null>(null)
|
||
const [dragOverObjectId, setDragOverObjectId] = useState<string | null>(null)
|
||
const orderedLayers = [...objects].reverse()
|
||
|
||
const getLayerIcon = (type: string) => {
|
||
switch (type) {
|
||
case 'text':
|
||
return <Text size={20} color="black" />
|
||
case 'rectangle':
|
||
return <Shapes size={20} color="black" />
|
||
case 'image':
|
||
return <Gallery size={20} color="black" />
|
||
case 'link':
|
||
return <Link2 size={20} color="black" />
|
||
case 'video':
|
||
return <VideoSquare size={20} color="black" />
|
||
case 'document':
|
||
return <DocumentText size={20} color="black" />
|
||
case 'arrow':
|
||
return <ArrowLeft size={20} color="black" />
|
||
case 'line':
|
||
return <Minus size={20} color="black" />
|
||
case 'grid':
|
||
return <Grid8 size={20} color="black" />
|
||
case 'group':
|
||
return <Layer size={20} color="black" />
|
||
case 'select':
|
||
return <MouseSquare size={20} color="black" />
|
||
case 'sticker':
|
||
return <Sticker size={20} color="black" />
|
||
default:
|
||
return <Shapes size={20} color="black" />
|
||
}
|
||
}
|
||
|
||
const getLayerLabel = (obj: typeof objects[0]) => {
|
||
if (obj.isMask) {
|
||
return 'ماسک'
|
||
}
|
||
|
||
switch (obj.type) {
|
||
case 'text':
|
||
return 'متن'
|
||
case 'rectangle': {
|
||
// نام واقعی شکل بر اساس shapeType
|
||
switch (obj.shapeType) {
|
||
case 'circle':
|
||
return 'دایره'
|
||
case 'triangle':
|
||
return 'مثلث'
|
||
case 'abstract':
|
||
return 'انتزاعی'
|
||
case 'square':
|
||
default:
|
||
return 'مربع'
|
||
}
|
||
}
|
||
case 'image':
|
||
return 'تصویر'
|
||
case 'link':
|
||
return 'لینک'
|
||
case 'video':
|
||
return 'ویدیو'
|
||
case 'document':
|
||
return 'سند'
|
||
case 'arrow':
|
||
return 'پیکان'
|
||
case 'line':
|
||
return 'خط'
|
||
case 'grid':
|
||
return 'گرید'
|
||
case 'group':
|
||
return 'گروه'
|
||
case 'select':
|
||
return 'انتخاب'
|
||
case 'sticker':
|
||
return 'استیکر'
|
||
default:
|
||
return 'لایه'
|
||
}
|
||
}
|
||
|
||
return (
|
||
<div className="flex-1 overflow-y-auto p-4">
|
||
<div className="flex flex-col gap-2">
|
||
{objects.length === 0 ? (
|
||
<div className="text-sm text-gray-500 text-center py-8">
|
||
لایهای وجود ندارد
|
||
</div>
|
||
) : (
|
||
orderedLayers.map((obj) => {
|
||
const isSelected = selectedObjectId === obj.id
|
||
return (
|
||
<div
|
||
key={obj.id}
|
||
onClick={() => setSelectedObjectId(obj.id)}
|
||
draggable
|
||
onDragStart={() => {
|
||
setDraggedObjectId(obj.id)
|
||
}}
|
||
onDragOver={(e) => {
|
||
e.preventDefault()
|
||
if (dragOverObjectId !== obj.id) {
|
||
setDragOverObjectId(obj.id)
|
||
}
|
||
}}
|
||
onDrop={(e) => {
|
||
e.preventDefault()
|
||
if (draggedObjectId && draggedObjectId !== obj.id) {
|
||
reorderObjects(draggedObjectId, obj.id)
|
||
}
|
||
setDraggedObjectId(null)
|
||
setDragOverObjectId(null)
|
||
}}
|
||
onDragEnd={() => {
|
||
setDraggedObjectId(null)
|
||
setDragOverObjectId(null)
|
||
}}
|
||
className={clx(
|
||
'flex items-center gap-3 p-3 rounded-lg cursor-pointer transition-colors',
|
||
dragOverObjectId === obj.id &&
|
||
draggedObjectId !== obj.id &&
|
||
'ring-2 ring-blue-300',
|
||
isSelected
|
||
? 'bg-blue-50 border border-blue-200'
|
||
: 'bg-gray-50 hover:bg-gray-100'
|
||
)}
|
||
>
|
||
<div className="shrink-0">{getLayerIcon(obj.type)}</div>
|
||
<div className="flex-1 text-xs">{getLayerLabel(obj)}</div>
|
||
<div className="flex items-center gap-1.5">
|
||
<button
|
||
onClick={(e) => {
|
||
e.stopPropagation()
|
||
toggleObjectVisibility(obj.id)
|
||
}}
|
||
className="w-6 h-6 flex items-center justify-center rounded-full hover:bg-gray-200 transition-colors"
|
||
>
|
||
{(obj.visible ?? true) ? (
|
||
<Eye size={16} color="black" />
|
||
) : (
|
||
<EyeSlash size={16} color="black" />
|
||
)}
|
||
</button>
|
||
<button
|
||
onClick={(e) => {
|
||
e.stopPropagation()
|
||
duplicateObject(obj.id)
|
||
}}
|
||
className="w-6 h-6 flex items-center justify-center rounded-full hover:bg-gray-200 transition-colors"
|
||
>
|
||
<Copy size={16} color="black" />
|
||
</button>
|
||
<button
|
||
onClick={(e) => {
|
||
e.stopPropagation()
|
||
deleteObject(obj.id)
|
||
}}
|
||
className="w-6 h-6 flex items-center justify-center rounded-full hover:bg-red-100 transition-colors"
|
||
>
|
||
<Trash size={16} color="#FF3B30" />
|
||
</button>
|
||
</div>
|
||
</div>
|
||
)
|
||
})
|
||
)}
|
||
</div>
|
||
</div>
|
||
)
|
||
}
|
||
|
||
export default LayersList
|
||
|