Files
dpage-editor/src/pages/editor/components/LayersList.tsx
T
2026-04-29 11:00:35 +03:30

207 lines
6.2 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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