diff --git a/apps/admin/src/components/category/CategoryHeroCarouselBlockForm.tsx b/apps/admin/src/components/category/CategoryHeroCarouselBlockForm.tsx index 73af690..961036d 100644 --- a/apps/admin/src/components/category/CategoryHeroCarouselBlockForm.tsx +++ b/apps/admin/src/components/category/CategoryHeroCarouselBlockForm.tsx @@ -265,6 +265,10 @@ export default function CategoryHeroCarouselBlockForm({ mobilePosition={getString(slide.mobileButtonPosition) || undefined} onChangeDesktop={(pos) => updateSlide(slideIndex, { buttonPosition: pos })} onChangeMobile={(pos) => updateSlide(slideIndex, { mobileButtonPosition: pos || '' })} + contentWidth={getString(slide.contentWidth, 'standard')} + onChangeContentWidth={(w) => updateSlide(slideIndex, { contentWidth: w })} + textColorScheme={getString(slide.textColorScheme, 'light')} + onChangeTextColorScheme={(scheme) => updateSlide(slideIndex, { textColorScheme: scheme })} title="ตำแหน่งปุ่ม & ข้อความของสไลด์นี้ (Slide Content Position)" /> diff --git a/apps/admin/src/components/page-builder/BannerForm.tsx b/apps/admin/src/components/page-builder/BannerForm.tsx index bd6814c..52550a6 100644 --- a/apps/admin/src/components/page-builder/BannerForm.tsx +++ b/apps/admin/src/components/page-builder/BannerForm.tsx @@ -106,6 +106,10 @@ export default function BannerForm({ mobilePosition={editingSection.payload?.mobileButtonPosition} onChangeDesktop={(pos) => updatePayloadValue('buttonPosition', pos)} onChangeMobile={(pos) => updatePayloadValue('mobileButtonPosition', pos)} + contentWidth={editingSection.payload?.contentWidth} + onChangeContentWidth={(w) => updatePayloadValue('contentWidth', w)} + textColorScheme={editingSection.payload?.textColorScheme} + onChangeTextColorScheme={(scheme) => updatePayloadValue('textColorScheme', scheme)} /> diff --git a/apps/admin/src/components/page-builder/ContentPositionPicker.tsx b/apps/admin/src/components/page-builder/ContentPositionPicker.tsx index 8ddbc28..06128cc 100644 --- a/apps/admin/src/components/page-builder/ContentPositionPicker.tsx +++ b/apps/admin/src/components/page-builder/ContentPositionPicker.tsx @@ -1,4 +1,4 @@ -import { Monitor, Smartphone, RotateCcw } from 'lucide-react'; +import { Monitor, Smartphone, RotateCcw, Maximize2, Sun, Moon, Palette } from 'lucide-react'; export const GRID_POSITIONS = [ { id: 'top-left', label: 'Top Left (บนซ้าย)', short: 'TL', icon: '↖' }, @@ -12,12 +12,39 @@ export const GRID_POSITIONS = [ { id: 'bottom-right', label: 'Bottom Right (ล่างขวา)', short: 'BR', icon: '↘' }, ]; +export const WIDTH_OPTIONS = [ + { id: 'full', label: 'ขอบนอกสุด (Full Bleed)', desc: 'เต็มความกว้างจอ ชิดขอบซ้าย/ขวา' }, + { id: 'standard', label: 'กรอบมาตรฐาน (1400px)', desc: 'ตามระยะเว็บหลัก max-w-7xl (แนะนำ)' }, + { id: 'compact', label: 'กรอบกระชับ (768px)', desc: 'กล่องกะทัดรัด max-w-3xl' }, +] as const; + +export const COLOR_SCHEME_OPTIONS = [ + { + id: 'light', + label: 'สว่าง (Light / ตัวอักษรสีขาว)', + desc: 'สำหรับภาพพื้นหลังโทนเข้ม หรือมี Overlay มืด (ค่าเริ่มต้น)', + icon: Sun, + badgeBg: 'bg-white/10 text-white border-white/20', + }, + { + id: 'dark', + label: 'มืด (Dark / ตัวอักษรสีเข้ม)', + desc: 'สำหรับภาพพื้นหลังโทนขาว หรือภาพสว่างสดใส', + icon: Moon, + badgeBg: 'bg-neutral-800 text-slate-200 border-white/10', + }, +] as const; + export interface ContentPositionPickerProps { desktopPosition?: string; mobilePosition?: string; defaultDesktop?: string; onChangeDesktop: (position: string) => void; onChangeMobile: (position: string | undefined) => void; + contentWidth?: string; + onChangeContentWidth?: (width: string) => void; + textColorScheme?: 'light' | 'dark' | string; + onChangeTextColorScheme?: (scheme: 'light' | 'dark') => void; title?: string; subtitle?: string; compact?: boolean; @@ -29,6 +56,10 @@ export default function ContentPositionPicker({ defaultDesktop = 'center', onChangeDesktop, onChangeMobile, + contentWidth, + onChangeContentWidth, + textColorScheme, + onChangeTextColorScheme, title = 'ตำแหน่งปุ่ม & เนื้อหา (Button & Content Position)', subtitle = 'สามารถแยกตำแหน่งระหว่าง Desktop (PC) และ Mobile (มือถือ) ได้อิสระ', compact = false, @@ -148,6 +179,82 @@ export default function ContentPositionPicker({ + + {/* Container Width / Outer Boundary Selection */} + {onChangeContentWidth && ( +
+
{description}
)} {buttonText && (+
{tagline}
)} {title && ( -+
{description}
)} @@ -265,7 +285,7 @@ export default function DynamicHero({ payload }: DynamicHeroProps) { )} {t('hero.learnMore')} → diff --git a/apps/spa/src/components/DynamicHeroCarousel.tsx b/apps/spa/src/components/DynamicHeroCarousel.tsx index 3a99fe2..d37eba6 100644 --- a/apps/spa/src/components/DynamicHeroCarousel.tsx +++ b/apps/spa/src/components/DynamicHeroCarousel.tsx @@ -4,7 +4,7 @@ import { Link } from 'react-router-dom'; import type { HeroCarouselSectionPayload } from '@homaroma/types'; import { useTranslation } from 'react-i18next'; import { getLocalizedField } from '../utils/localization'; -import { getHeroPositionClasses } from './DynamicHero'; +import { getHeroPositionClasses, getHeroContainerWidthClasses } from './DynamicHero'; // Import Swiper styles import 'swiper/css'; @@ -119,28 +119,34 @@ export default function DynamicHeroCarousel({ payload }: DynamicHeroCarouselProp {(slide.title || slide.subtitle || slide.buttonText) && (() => { const slideDesktopPos = (slide as any).buttonPosition || 'bottom-left'; const slideMobilePos = (slide as any).mobileButtonPosition || slideDesktopPos; + const slideWidth = (slide as any).contentWidth || 'standard'; + const slideColor = (slide as any).textColorScheme || 'light'; + const isDarkSlide = slideColor === 'dark'; const { wrapperClasses, contentClasses, actionsClasses } = getHeroPositionClasses(slideMobilePos, slideDesktopPos); + const containerClasses = getHeroContainerWidthClasses(slideWidth); return ( -- {slide.subtitle} -
- )} - {slide.title && ( -+ {slide.subtitle} +
+ )} + {slide.title && ( +- {tagline} -
- )} + <> + -+ {tagline} +
+ )} - {description && ( -- {description} -
- )} ++ {description} +
+ )} - {secondaryButtonText && payload.secondaryButtonLink && ( - - {secondaryButtonText} - - )} -+
{tagline}
)} {title && ( -+
{description}
)} @@ -333,7 +350,7 @@ function HeroSection({ payload, lang }: { payload: Record{subtitle}
} - {title &&{subtitle}
} + {title &&