345 lines
22 KiB
TypeScript
345 lines
22 KiB
TypeScript
import { FC } from 'react'
|
||
import { usePersonalityStore } from '../store/Store'
|
||
import { AddCircle } from 'iconsax-react'
|
||
import TextRenderer from './TextRenderer'
|
||
import ButtonRenderer from './ButtonRenderer'
|
||
import ImageRenderer from './ImageRenderer'
|
||
import SocialRenderer from './SocialRenderer'
|
||
import { VerticalAlignment, HorizontalAlignment } from '../types/Types'
|
||
|
||
interface GenericSectionProps {
|
||
sectionKey: string
|
||
}
|
||
|
||
const GenericSection: FC<GenericSectionProps> = ({ sectionKey }) => {
|
||
|
||
const { activeSection, activeItemIndex, data, setActiveSection, setActiveItemIndex } = usePersonalityStore()
|
||
|
||
// Helper function to get numeric height
|
||
const getNumericHeight = (height?: string) => {
|
||
return parseInt(height?.replace(/px|em|rem|%/g, '') || "123")
|
||
}
|
||
|
||
// Helper function to generate border style object based on selected sides
|
||
const generateBorderStyle = (border?: { width?: string; color?: string; style?: string; sides?: string[] }) => {
|
||
if (!border || border.style === 'none') {
|
||
return {}
|
||
}
|
||
|
||
const { width = '0px', color = '#E5E7EB', style = 'solid', sides = [] } = border
|
||
|
||
// اگر width صفر باشد یا تعریف نشده باشد، border نمایش نده
|
||
if (!width || width === '0px') {
|
||
return {}
|
||
}
|
||
|
||
if (sides.length === 0 || sides.length === 4) {
|
||
return { border: `${width} ${style} ${color}` }
|
||
}
|
||
|
||
// Generate individual border sides
|
||
const borderStyle: any = {}
|
||
sides.forEach(side => {
|
||
borderStyle[`border${side.charAt(0).toUpperCase() + side.slice(1)}`] = `${width} ${style} ${color}`
|
||
})
|
||
|
||
return borderStyle
|
||
}
|
||
|
||
// Early return if data is not available
|
||
if (!data || !data[sectionKey]) {
|
||
return (
|
||
<tr>
|
||
<td style={{ height: '123px', textAlign: 'center', verticalAlign: 'middle' }}>
|
||
<div>در حال بارگذاری...</div>
|
||
</td>
|
||
</tr>
|
||
)
|
||
}
|
||
|
||
const sectionData = data[sectionKey]
|
||
|
||
const handleSectionClick = (section: string, e: React.MouseEvent) => {
|
||
|
||
// اگر کلیک روی فضای خالی باشد (نه روی content elements)
|
||
const target = e.target as HTMLElement;
|
||
const isClickOnContent = target.closest('[data-element-type]') !== null;
|
||
|
||
if (!isClickOnContent) {
|
||
setActiveSection(section)
|
||
}
|
||
}
|
||
|
||
const handleItemClick = (index: number, e: React.MouseEvent) => {
|
||
|
||
// Check if click is on a content element (text, button, image, social)
|
||
const target = e.target as HTMLElement;
|
||
const isClickOnContent = target.closest('[data-element-type]') !== null;
|
||
|
||
|
||
// Only select column if not clicking on content elements
|
||
if (!isClickOnContent) {
|
||
|
||
e.stopPropagation()
|
||
|
||
// Only set active section if we're not already in this section
|
||
if (activeSection !== sectionKey) {
|
||
|
||
setActiveSection(sectionKey)
|
||
}
|
||
|
||
// Always set the active item index
|
||
setActiveItemIndex(index)
|
||
}
|
||
}
|
||
|
||
// تابعهای helper برای تعیین alignment دکمهها
|
||
const getButtonHorizontalAlign = (button?: { alignment?: HorizontalAlignment }) => {
|
||
if (!button || !button.alignment) return 'center'
|
||
switch (button.alignment) {
|
||
case HorizontalAlignment.LEFT:
|
||
return 'left'
|
||
case HorizontalAlignment.RIGHT:
|
||
return 'right'
|
||
case HorizontalAlignment.CENTER:
|
||
default:
|
||
return 'center'
|
||
}
|
||
}
|
||
|
||
const getButtonVerticalAlign = (button?: { verticalAlignment?: VerticalAlignment }) => {
|
||
if (!button || !button.verticalAlignment) return 'middle'
|
||
switch (button.verticalAlignment) {
|
||
case VerticalAlignment.TOP:
|
||
return 'top'
|
||
case VerticalAlignment.BOTTOM:
|
||
return 'bottom'
|
||
case VerticalAlignment.MIDDLE:
|
||
default:
|
||
return 'middle'
|
||
}
|
||
}
|
||
|
||
return (
|
||
<tr onClick={(e) => handleSectionClick(sectionKey, e)}>
|
||
{
|
||
sectionData.columnsCount > 0 ?
|
||
<td style={{
|
||
padding: '10px',
|
||
...(() => {
|
||
const borderStyle = generateBorderStyle(sectionData.border)
|
||
if (activeSection === sectionKey) {
|
||
// اگر سکشن انتخاب شده است، border آبی + border سکشن را نمایش بده
|
||
if (Object.keys(borderStyle).length === 0) {
|
||
return { border: '1px dashed #0038FF' }
|
||
} else {
|
||
return {
|
||
...borderStyle,
|
||
outline: '1px dashed #0038FF',
|
||
outlineOffset: '2px'
|
||
}
|
||
}
|
||
} else {
|
||
// اگر سکشن انتخاب نشده، فقط border سکشن یا border پیشفرض
|
||
if (Object.keys(borderStyle).length === 0) {
|
||
return { border: '1px dashed #E5E7EB' }
|
||
}
|
||
return borderStyle
|
||
}
|
||
})(),
|
||
borderRadius: '24px',
|
||
cursor: 'pointer',
|
||
height: sectionData.height || '123px'
|
||
}}>
|
||
<table width="100%" cellPadding="0" cellSpacing="0" border={0} style={{ tableLayout: 'fixed' }}>
|
||
<tbody>
|
||
<tr>
|
||
{
|
||
Array.from({ length: sectionData.columnsCount }, (_, index) => {
|
||
const item = sectionData.items[index];
|
||
const isActive = activeSection === sectionKey && activeItemIndex === index;
|
||
|
||
return (
|
||
<>
|
||
<td
|
||
key={index}
|
||
onClick={(e) => handleItemClick(index, e)}
|
||
style={{
|
||
width: `${100 / sectionData.columnsCount}%`,
|
||
height: sectionData.height || '123px',
|
||
border: isActive ? '1px dashed #0038FF' : '1px dashed #EAECF4',
|
||
backgroundColor: item?.backgroundColor || '#ffffff',
|
||
backgroundImage: item?.backgroundImage ? `url(${item.backgroundImage})` : 'none',
|
||
backgroundSize: item?.style?.backgroundSize || 'cover',
|
||
backgroundRepeat: item?.style?.backgroundRepeat || 'no-repeat',
|
||
backgroundPosition: item?.style?.backgroundPosition || 'center',
|
||
cursor: 'pointer',
|
||
// padding: '8px',
|
||
verticalAlign: 'top',
|
||
borderRadius: '8px',
|
||
position: 'relative'
|
||
}}
|
||
>
|
||
<table width="100%" cellPadding="0" cellSpacing="0" border={0} style={{
|
||
borderCollapse: 'collapse',
|
||
height: sectionData.height || '123px'
|
||
}}>
|
||
<tbody>
|
||
{/* اگر column خالی است */}
|
||
{(!item?.texts || item.texts.length === 0) &&
|
||
(!item?.buttons || item.buttons.length === 0) &&
|
||
(!item?.images || item.images.length === 0) &&
|
||
(!item?.socials || item.socials.length === 0) ? (
|
||
<tr>
|
||
<td
|
||
width="100%"
|
||
height={getNumericHeight(sectionData.height).toString()}
|
||
style={{
|
||
padding: '8px',
|
||
cursor: 'pointer'
|
||
}}
|
||
onClick={(e) => handleItemClick(index, e)}
|
||
>
|
||
{/* محتوای خالی برای columns خالی */}
|
||
</td>
|
||
</tr>
|
||
) : (
|
||
<>
|
||
{/* اگر فقط دکمه داریم و متن نداریم */}
|
||
{(!item?.texts || item.texts.length === 0) && item?.buttons && item.buttons.length > 0 ? (
|
||
<tr>
|
||
<td
|
||
width="100%"
|
||
height={getNumericHeight(sectionData.height).toString()}
|
||
align={getButtonHorizontalAlign(item.buttons[0])}
|
||
valign={getButtonVerticalAlign(item.buttons[0])}
|
||
style={{
|
||
padding: '8px'
|
||
}}
|
||
>
|
||
<ButtonRenderer
|
||
buttons={item.buttons}
|
||
itemId={item?.id || ''}
|
||
sectionKey={sectionKey}
|
||
/>
|
||
</td>
|
||
</tr>
|
||
) : (
|
||
<>
|
||
{/* ردیف اصلی برای متن */}
|
||
<tr>
|
||
<td
|
||
width="100%"
|
||
height={item?.buttons && item.buttons.length > 0 ?
|
||
Math.floor(getNumericHeight(sectionData.height) * 0.6).toString() :
|
||
Math.floor(getNumericHeight(sectionData.height) * 0.8).toString()}
|
||
align={item?.texts?.[0]?.alignment || 'center'}
|
||
valign={item?.texts?.[0]?.verticalAlignment === VerticalAlignment.TOP ? 'top' :
|
||
item?.texts?.[0]?.verticalAlignment === VerticalAlignment.BOTTOM ? 'bottom' : 'middle'}
|
||
style={{
|
||
padding: '8px',
|
||
fontSize: '14px',
|
||
lineHeight: '1.4'
|
||
}}
|
||
>
|
||
<TextRenderer
|
||
texts={item?.texts || []}
|
||
itemId={item?.id || ''}
|
||
sectionKey={sectionKey}
|
||
/>
|
||
<ImageRenderer
|
||
images={item?.images || []}
|
||
itemId={item?.id || ''}
|
||
sectionKey={sectionKey}
|
||
/>
|
||
<SocialRenderer
|
||
socials={item?.socials || []}
|
||
itemId={item?.id || ''}
|
||
sectionKey={sectionKey}
|
||
/>
|
||
</td>
|
||
</tr>
|
||
{/* ردیف دکمهها */}
|
||
{item?.buttons && item.buttons.length > 0 && (
|
||
<tr>
|
||
<td
|
||
width="100%"
|
||
height={Math.floor(getNumericHeight(sectionData.height) * 0.2).toString()}
|
||
align={getButtonHorizontalAlign(item.buttons[0])}
|
||
valign={getButtonVerticalAlign(item.buttons[0])}
|
||
style={{
|
||
padding: '0 8px 8px 8px'
|
||
}}
|
||
>
|
||
<ButtonRenderer
|
||
buttons={item.buttons}
|
||
itemId={item?.id || ''}
|
||
sectionKey={sectionKey}
|
||
/>
|
||
</td>
|
||
</tr>
|
||
)}
|
||
</>
|
||
)}
|
||
</>
|
||
)}
|
||
</tbody>
|
||
</table>
|
||
</td>
|
||
{index < sectionData.columnsCount - 1 && (
|
||
<td style={{ width: '16px' }}></td>
|
||
)}
|
||
</>
|
||
)
|
||
})
|
||
}
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
</td>
|
||
:
|
||
<td
|
||
onClick={(e) => handleSectionClick(sectionKey, e)}
|
||
style={{
|
||
height: sectionData.height || '123px',
|
||
...(() => {
|
||
const borderStyle = generateBorderStyle(sectionData.border)
|
||
if (activeSection === sectionKey) {
|
||
// اگر سکشن انتخاب شده است، border آبی + border سکشن را نمایش بده
|
||
if (Object.keys(borderStyle).length === 0) {
|
||
return { border: '1px dashed #0038FF' }
|
||
} else {
|
||
return {
|
||
...borderStyle,
|
||
outline: '1px dashed #0038FF',
|
||
outlineOffset: '2px'
|
||
}
|
||
}
|
||
} else {
|
||
// اگر سکشن انتخاب نشده، فقط border سکشن یا border پیشفرض
|
||
if (Object.keys(borderStyle).length === 0) {
|
||
return { border: '1px dashed #E5E7EB' }
|
||
}
|
||
return borderStyle
|
||
}
|
||
})(),
|
||
borderRadius: '24px',
|
||
textAlign: 'center',
|
||
verticalAlign: 'middle',
|
||
padding: '10px',
|
||
cursor: 'pointer'
|
||
}}
|
||
>
|
||
<div style={{ color: '#888', fontSize: '14px', marginBottom: '8px' }}>
|
||
{data[sectionKey]?.name || sectionKey}
|
||
</div>
|
||
<div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center' }}>
|
||
<AddCircle size={24} color='#888' />
|
||
</div>
|
||
</td>
|
||
}
|
||
</tr>
|
||
)
|
||
}
|
||
|
||
export default GenericSection |