x
This commit is contained in:
1 parent
dba82b26a8
commit
943fd8f1a3
16 files changed
+257
-20
No files matched your search
@@ -110,6 +110,8 @@ export default function BannerForm({
|
||||
onChangeContentWidth={(w) => updatePayloadValue('contentWidth', w)}
|
||||
textColorScheme={editingSection.payload?.textColorScheme}
|
||||
onChangeTextColorScheme={(scheme) => updatePayloadValue('textColorScheme', scheme)}
|
||||
titleSize={editingSection.payload?.titleSize}
|
||||
onChangeTitleSize={(size) => updatePayloadValue('titleSize', size)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Monitor, Smartphone, RotateCcw, Maximize2, Sun, Moon, Palette } from 'lucide-react';
|
||||
import { Monitor, Smartphone, RotateCcw, Maximize2, Sun, Moon, Palette, Type } from 'lucide-react';
|
||||
|
||||
export const GRID_POSITIONS = [
|
||||
{ id: 'top-left', label: 'Top Left (บนซ้าย)', short: 'TL', icon: '↖' },
|
||||
@@ -18,6 +18,13 @@ export const WIDTH_OPTIONS = [
|
||||
{ id: 'compact', label: 'กรอบกระชับ (768px)', desc: 'กล่องกะทัดรัด max-w-3xl' },
|
||||
] as const;
|
||||
|
||||
export const TITLE_SIZE_OPTIONS = [
|
||||
{ id: 'sm', label: 'กะทัดรัด (Small / 44px)', desc: 'เหมาะกับข้อความยาว หรือภาพที่มีสินค้าเด่นด้านข้าง ไม่ชนสินค้า' },
|
||||
{ id: 'md', label: 'ปานกลาง (Medium / 60px)', desc: 'ขนาดมาตรฐานบทความ / Heading ชัดเจน ไม่ล้นจอ' },
|
||||
{ id: 'lg', label: 'ใหญ่ (Large / 76px)', desc: 'ขนาดโปรโมชัน เน้นตัวอักษรเด่นและสะดุดตา' },
|
||||
{ id: 'xl', label: 'ยักษ์ (Display / 96px)', desc: 'ขนาดดั้งเดิม (Display Huge) เหมาะกับคำสั้น 2-3 คำ' },
|
||||
] as const;
|
||||
|
||||
export const COLOR_SCHEME_OPTIONS = [
|
||||
{
|
||||
id: 'light',
|
||||
@@ -45,6 +52,8 @@ export interface ContentPositionPickerProps {
|
||||
onChangeContentWidth?: (width: string) => void;
|
||||
textColorScheme?: 'light' | 'dark' | string;
|
||||
onChangeTextColorScheme?: (scheme: 'light' | 'dark') => void;
|
||||
titleSize?: 'sm' | 'md' | 'lg' | 'xl' | string;
|
||||
onChangeTitleSize?: (size: string) => void;
|
||||
title?: string;
|
||||
subtitle?: string;
|
||||
compact?: boolean;
|
||||
@@ -60,6 +69,8 @@ export default function ContentPositionPicker({
|
||||
onChangeContentWidth,
|
||||
textColorScheme,
|
||||
onChangeTextColorScheme,
|
||||
titleSize,
|
||||
onChangeTitleSize,
|
||||
title = 'ตำแหน่งปุ่ม & เนื้อหา (Button & Content Position)',
|
||||
subtitle = 'สามารถแยกตำแหน่งระหว่าง Desktop (PC) และ Mobile (มือถือ) ได้อิสระ',
|
||||
compact = false,
|
||||
@@ -255,6 +266,43 @@ export default function ContentPositionPicker({
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Title Size / Heading Scale Preset Selection */}
|
||||
{onChangeTitleSize && (
|
||||
<div className="bg-night-950/40 border border-white/5 p-3 rounded-2xl space-y-2">
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-[11px] font-bold uppercase tracking-wider text-slate-300 flex items-center gap-1.5">
|
||||
<Type className="w-3.5 h-3.5 text-cyan-glow" />
|
||||
ขนาดตัวอักษรหัวข้อ (Title Font Size Preset)
|
||||
</span>
|
||||
<span className="px-2 py-0.5 rounded-md bg-cyan-glow/10 text-cyan-glow font-mono font-bold text-[10px] border border-cyan-glow/20">
|
||||
{(titleSize || 'xl').toUpperCase()}
|
||||
</span>
|
||||
</div>
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-2">
|
||||
{TITLE_SIZE_OPTIONS.map((opt) => {
|
||||
const isSelected = (titleSize || 'xl') === opt.id;
|
||||
return (
|
||||
<button
|
||||
key={opt.id}
|
||||
type="button"
|
||||
onClick={() => onChangeTitleSize(opt.id)}
|
||||
className={`px-3 py-2.5 rounded-xl text-left transition-all border cursor-pointer ${
|
||||
isSelected
|
||||
? 'bg-cyan-glow/15 border-cyan-glow/50 text-white shadow-sm ring-1 ring-cyan-glow/30'
|
||||
: 'bg-night-900 border-white/5 text-slate-400 hover:text-white hover:border-white/10'
|
||||
}`}
|
||||
>
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-xs font-bold leading-tight">{opt.label}</span>
|
||||
</div>
|
||||
<div className="text-[9px] text-slate-500 mt-1 leading-tight">{opt.desc}</div>
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -72,6 +72,20 @@ export default function HeroArticleForm({
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<label className="text-[10px] uppercase font-bold tracking-widest text-slate-400">Title Font Size</label>
|
||||
<select
|
||||
value={editingSection.payload?.titleSize || 'xl'}
|
||||
onChange={(e) => updatePayloadValue('titleSize', e.target.value)}
|
||||
className="admin-input w-full bg-slate-900 border-white/10"
|
||||
>
|
||||
<option value="sm">Small (กะทัดรัด / 44px)</option>
|
||||
<option value="md">Medium (ปานกลาง / 60px)</option>
|
||||
<option value="lg">Large (ใหญ่ / 76px)</option>
|
||||
<option value="xl">Display (ยักษ์ / 96px)</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<label className="text-[10px] uppercase font-bold tracking-widest text-slate-400">Author</label>
|
||||
<input
|
||||
|
||||
@@ -236,6 +236,8 @@ export default function HeroForm({
|
||||
onChangeContentWidth={(w) => updatePayloadValue('contentWidth', w)}
|
||||
textColorScheme={editingSection.payload?.textColorScheme}
|
||||
onChangeTextColorScheme={(scheme) => updatePayloadValue('textColorScheme', scheme)}
|
||||
titleSize={editingSection.payload?.titleSize}
|
||||
onChangeTitleSize={(size) => updatePayloadValue('titleSize', size)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -71,6 +71,20 @@ export default function HeroV2Form({
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<label className="text-[10px] uppercase font-bold tracking-widest text-slate-400">Title Font Size</label>
|
||||
<select
|
||||
value={editingSection.payload?.titleSize || 'lg'}
|
||||
onChange={(e) => updatePayloadValue('titleSize', e.target.value)}
|
||||
className="admin-input w-full bg-slate-900 border-white/10"
|
||||
>
|
||||
<option value="sm">Small (กะทัดรัด / 44px)</option>
|
||||
<option value="md">Medium (ปานกลาง / 60px)</option>
|
||||
<option value="lg">Large (ใหญ่ / 76px)</option>
|
||||
<option value="xl">Display (ยักษ์ / 96px)</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2 flex items-center gap-2 pt-6">
|
||||
<input
|
||||
type="checkbox"
|
||||
|
||||
@@ -303,6 +303,8 @@ export default function HeroVideoForm({
|
||||
onChangeContentWidth={(w) => updatePayloadValue('contentWidth', w)}
|
||||
textColorScheme={payload.textColorScheme}
|
||||
onChangeTextColorScheme={(scheme) => updatePayloadValue('textColorScheme', scheme)}
|
||||
titleSize={payload.titleSize}
|
||||
onChangeTitleSize={(size) => updatePayloadValue('titleSize', size)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -17,6 +17,10 @@ const validHeroCarouselPayload = {
|
||||
subtitle: { th: "ข้อความรอง", en: "Supporting copy" },
|
||||
buttonText: { th: "ดูสินค้า", en: "View product" },
|
||||
focalPoint: { x: "50%", y: "45%" },
|
||||
buttonPosition: "bottom-left",
|
||||
contentWidth: "standard",
|
||||
textColorScheme: "light",
|
||||
titleSize: "xl",
|
||||
},
|
||||
],
|
||||
autoplay: true,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Elysia, t } from "elysia";
|
||||
import prisma from "../lib/prisma";
|
||||
// Trigger reload: support containerWidth, cardSize, and layout for PRODUCT_CAROUSEL and PRODUCTS
|
||||
// Trigger reload: support titleSize in HERO and HERO_VIDEO payloads
|
||||
import {
|
||||
LocalizedStringSchema,
|
||||
HeroSectionPayloadSchema,
|
||||
|
||||
@@ -89,8 +89,8 @@ const PAGE_BLOCK_METADATA: Record<string, BlockMetadata> = {
|
||||
HERO: {
|
||||
type: "HERO",
|
||||
requiredFields: ["tagline", "title", "description", "imageUrl", "buttonText", "buttonLink"],
|
||||
optionalFields: ["focalPoint"],
|
||||
renderedFields: ["tagline", "title", "description", "imageUrl", "buttonText", "buttonLink", "focalPoint"],
|
||||
optionalFields: ["focalPoint", "contentWidth", "textColorScheme", "buttonPosition", "mobileButtonPosition", "overlayStyle", "overlayOpacity", "titleSize"],
|
||||
renderedFields: ["tagline", "title", "description", "imageUrl", "buttonText", "buttonLink", "focalPoint", "contentWidth", "textColorScheme", "buttonPosition", "mobileButtonPosition", "overlayStyle", "overlayOpacity", "titleSize"],
|
||||
storedOnlyFields: [],
|
||||
discoveryToolsNeeded: [],
|
||||
warnings: ["Use an image URL from a host allowed by apps/website next.config.ts."],
|
||||
@@ -669,8 +669,8 @@ const PAGE_BLOCK_METADATA: Record<string, BlockMetadata> = {
|
||||
HERO_V2: {
|
||||
type: "HERO_V2",
|
||||
requiredFields: ["title", "imageUrl"],
|
||||
optionalFields: ["tagline", "description", "contentAlignment", "showOrnament", "primaryButtonText", "primaryButtonLink", "secondaryButtonText", "secondaryButtonLink", "focalPoint"],
|
||||
renderedFields: ["tagline", "title", "description", "imageUrl", "contentAlignment", "showOrnament", "primaryButtonText", "primaryButtonLink", "secondaryButtonText", "secondaryButtonLink", "focalPoint"],
|
||||
optionalFields: ["tagline", "description", "contentAlignment", "showOrnament", "primaryButtonText", "primaryButtonLink", "secondaryButtonText", "secondaryButtonLink", "focalPoint", "titleSize"],
|
||||
renderedFields: ["tagline", "title", "description", "imageUrl", "contentAlignment", "showOrnament", "primaryButtonText", "primaryButtonLink", "secondaryButtonText", "secondaryButtonLink", "focalPoint", "titleSize"],
|
||||
storedOnlyFields: [],
|
||||
discoveryToolsNeeded: [],
|
||||
warnings: ["contentAlignment must be left, center, or right. Use an image URL allowed by apps/website."],
|
||||
|
||||
@@ -151,6 +151,20 @@ export function getHeroContainerWidthClasses(contentWidth?: string) {
|
||||
}
|
||||
}
|
||||
|
||||
export function getHeroTitleSizeClass(titleSize?: string): string {
|
||||
switch (titleSize) {
|
||||
case 'sm':
|
||||
return 'ui-hero-title-sm';
|
||||
case 'md':
|
||||
return 'ui-hero-title-md';
|
||||
case 'lg':
|
||||
return 'ui-hero-title-lg';
|
||||
case 'xl':
|
||||
default:
|
||||
return 'ui-hero-title-xl';
|
||||
}
|
||||
}
|
||||
|
||||
export default function DynamicHero({ payload }: DynamicHeroProps) {
|
||||
const { t, i18n } = useTranslation();
|
||||
const lang = i18n.language;
|
||||
@@ -240,6 +254,9 @@ export default function DynamicHero({ payload }: DynamicHeroProps) {
|
||||
};
|
||||
|
||||
const containerClasses = getHeroContainerWidthClasses(contentWidth);
|
||||
const titleSize = (payload as any).titleSize || 'xl';
|
||||
const titleSizeClass = getHeroTitleSizeClass(titleSize);
|
||||
const textWrapperWidthClass = titleSize === 'sm' || contentWidth === 'compact' ? 'max-w-xl' : titleSize === 'md' ? 'max-w-2xl' : 'max-w-3xl';
|
||||
|
||||
return (
|
||||
<section className={`relative w-full min-h-[85vh] sm:min-h-[90vh] md:h-[90vh] md:aspect-[21/9] flex overflow-hidden ${isDarkText ? 'bg-surface-page' : 'bg-night-950'}`}>
|
||||
@@ -258,14 +275,14 @@ export default function DynamicHero({ payload }: DynamicHeroProps) {
|
||||
</div>
|
||||
|
||||
<div className={`relative z-10 min-h-[85vh] sm:min-h-[90vh] md:min-h-full flex flex-col ${containerClasses} ${wrapperClasses} animate-fade-in`}>
|
||||
<div className={`w-full max-w-3xl flex flex-col space-y-5 ${contentClasses}`}>
|
||||
<div className={`w-full ${textWrapperWidthClass} flex flex-col space-y-5 ${contentClasses}`}>
|
||||
{tagline && (
|
||||
<p className={`${isDarkText ? 'text-pop-pink' : 'text-pop-yellow'} font-semibold tracking-[0.3em] text-xs md:text-sm uppercase drop-shadow-md`}>
|
||||
{tagline}
|
||||
</p>
|
||||
)}
|
||||
{title && (
|
||||
<h1 className={`ui-hero-title ${isDarkText ? '!text-pop-black' : '!text-white'} drop-shadow-lg`}>
|
||||
<h1 className={`ui-hero-title ${titleSizeClass} ${isDarkText ? '!text-pop-black' : '!text-white'} drop-shadow-lg`}>
|
||||
{renderTitle(title)}
|
||||
</h1>
|
||||
)}
|
||||
|
||||
@@ -2,6 +2,7 @@ import { Link } from 'react-router-dom';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { getLocalizedField } from '../utils/localization';
|
||||
import { Sparkles, ChevronRight } from 'lucide-react';
|
||||
import { getHeroTitleSizeClass } from './DynamicHero';
|
||||
|
||||
interface DynamicHeroV2Props {
|
||||
payload: Record<string, any>;
|
||||
@@ -68,7 +69,7 @@ export default function DynamicHeroV2({ payload }: DynamicHeroV2Props) {
|
||||
</div>
|
||||
)}
|
||||
|
||||
<h1 className="text-4xl md:text-5xl lg:text-6xl font-serif text-pop-black leading-tight">
|
||||
<h1 className={`font-serif ui-hero-title ${getHeroTitleSizeClass((payload as any).titleSize)} text-pop-black leading-tight`}>
|
||||
{renderTitle(title || '')}
|
||||
</h1>
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import { Link } from 'react-router-dom';
|
||||
import type { HeroVideoSectionPayload } from '@homaroma/types';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { getLocalizedField } from '../utils/localization';
|
||||
import { getHeroPositionClasses, getHeroContainerWidthClasses } from './DynamicHero';
|
||||
import { getHeroPositionClasses, getHeroContainerWidthClasses, getHeroTitleSizeClass } from './DynamicHero';
|
||||
|
||||
interface DynamicHeroVideoProps {
|
||||
payload: HeroVideoSectionPayload;
|
||||
@@ -98,6 +98,9 @@ export default function DynamicHeroVideo({ payload }: DynamicHeroVideoProps) {
|
||||
const desktopPosition = (payload as any).buttonPosition || 'center';
|
||||
const mobilePosition = (payload as any).mobileButtonPosition || desktopPosition;
|
||||
const contentWidth = (payload as any).contentWidth || 'standard';
|
||||
const titleSize = (payload as any).titleSize || 'xl';
|
||||
const titleSizeClass = getHeroTitleSizeClass(titleSize);
|
||||
const textWrapperWidthClass = titleSize === 'sm' || contentWidth === 'compact' ? 'max-w-xl' : titleSize === 'md' ? 'max-w-2xl' : 'max-w-3xl';
|
||||
const { wrapperClasses, contentClasses, actionsClasses } = getHeroPositionClasses(mobilePosition, desktopPosition);
|
||||
const containerClasses = getHeroContainerWidthClasses(contentWidth);
|
||||
|
||||
@@ -109,14 +112,14 @@ export default function DynamicHeroVideo({ payload }: DynamicHeroVideoProps) {
|
||||
></div>
|
||||
|
||||
<div className={`relative z-20 py-16 h-full flex flex-col animate-fade-in ${containerClasses} ${wrapperClasses}`}>
|
||||
<div className={`flex flex-col space-y-6 max-w-3xl ${contentClasses}`}>
|
||||
<div className={`flex flex-col space-y-6 ${textWrapperWidthClass} ${contentClasses}`}>
|
||||
{tagline && (
|
||||
<p className="text-xs md:text-sm font-semibold uppercase tracking-[0.35em] text-pop-pink drop-shadow-md">
|
||||
{tagline}
|
||||
</p>
|
||||
)}
|
||||
|
||||
<h1 className={`font-serif text-4xl md:text-6xl lg:text-7xl font-light leading-tight tracking-wide drop-shadow-lg ${isDarkText ? 'text-pop-black' : 'text-white'}`}>
|
||||
<h1 className={`font-serif ui-hero-title ${titleSizeClass} font-light leading-tight tracking-wide drop-shadow-lg ${isDarkText ? '!text-pop-black' : '!text-white'}`}>
|
||||
{renderTitle(title) || renderTitle(t('hero.defaultTitle'))}
|
||||
</h1>
|
||||
|
||||
|
||||
@@ -148,6 +148,22 @@
|
||||
color: var(--color-pop-black);
|
||||
}
|
||||
|
||||
.ui-hero-title-sm {
|
||||
@apply !text-2xl sm:!text-3xl md:!text-4xl lg:!text-5xl;
|
||||
}
|
||||
|
||||
.ui-hero-title-md {
|
||||
@apply !text-3xl sm:!text-4xl md:!text-5xl lg:!text-6xl;
|
||||
}
|
||||
|
||||
.ui-hero-title-lg {
|
||||
@apply !text-4xl sm:!text-5xl md:!text-6xl lg:!text-7xl;
|
||||
}
|
||||
|
||||
.ui-hero-title-xl {
|
||||
@apply !text-5xl md:!text-7xl lg:!text-8xl;
|
||||
}
|
||||
|
||||
.ui-hero-copy {
|
||||
@apply mx-auto max-w-2xl pb-8 pt-4 text-lg font-light drop-shadow-md md:text-xl;
|
||||
color: var(--color-pop-black);
|
||||
|
||||
@@ -451,6 +451,26 @@
|
||||
filter: drop-shadow(0 4px 3px rgb(0 0 0 / 0.07)) drop-shadow(0 2px 2px rgb(0 0 0 / 0.06));
|
||||
}
|
||||
|
||||
.ui-hero-title-sm {
|
||||
font-size: 1.75rem !important;
|
||||
line-height: 1.2 !important;
|
||||
}
|
||||
|
||||
.ui-hero-title-md {
|
||||
font-size: 2.25rem !important;
|
||||
line-height: 1.2 !important;
|
||||
}
|
||||
|
||||
.ui-hero-title-lg {
|
||||
font-size: 2.75rem !important;
|
||||
line-height: 1.15 !important;
|
||||
}
|
||||
|
||||
.ui-hero-title-xl {
|
||||
font-size: 3rem !important;
|
||||
line-height: 1.25 !important;
|
||||
}
|
||||
|
||||
.ui-hero-copy {
|
||||
margin-inline: auto;
|
||||
max-width: 42rem;
|
||||
@@ -568,6 +588,26 @@
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.ui-hero-title-sm {
|
||||
font-size: 2.25rem !important;
|
||||
line-height: 1.15 !important;
|
||||
}
|
||||
|
||||
.ui-hero-title-md {
|
||||
font-size: 3rem !important;
|
||||
line-height: 1.15 !important;
|
||||
}
|
||||
|
||||
.ui-hero-title-lg {
|
||||
font-size: 3.75rem !important;
|
||||
line-height: 1.1 !important;
|
||||
}
|
||||
|
||||
.ui-hero-title-xl {
|
||||
font-size: 4.5rem !important;
|
||||
line-height: 1 !important;
|
||||
}
|
||||
|
||||
.ui-hero-copy {
|
||||
font-size: 1.25rem;
|
||||
line-height: 1.75rem;
|
||||
@@ -588,6 +628,26 @@
|
||||
font-size: 6rem;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.ui-hero-title-sm {
|
||||
font-size: 2.75rem !important;
|
||||
line-height: 1.1 !important;
|
||||
}
|
||||
|
||||
.ui-hero-title-md {
|
||||
font-size: 3.75rem !important;
|
||||
line-height: 1.05 !important;
|
||||
}
|
||||
|
||||
.ui-hero-title-lg {
|
||||
font-size: 4.75rem !important;
|
||||
line-height: 1 !important;
|
||||
}
|
||||
|
||||
.ui-hero-title-xl {
|
||||
font-size: 6rem !important;
|
||||
line-height: 1 !important;
|
||||
}
|
||||
}
|
||||
|
||||
.ui-panel {
|
||||
|
||||
@@ -273,6 +273,35 @@ function getHeroContainerWidthClasses(contentWidth?: string) {
|
||||
}
|
||||
}
|
||||
|
||||
export function getHeroTitleSizeClass(titleSize?: unknown): string {
|
||||
switch (titleSize) {
|
||||
case 'sm':
|
||||
return 'ui-hero-title-sm'
|
||||
case 'md':
|
||||
return 'ui-hero-title-md'
|
||||
case 'lg':
|
||||
return 'ui-hero-title-lg'
|
||||
case 'xl':
|
||||
default:
|
||||
return 'ui-hero-title-xl'
|
||||
}
|
||||
}
|
||||
|
||||
export function getTailwindHeroTitleClass(titleSize?: unknown, defaultSize = 'xl'): string {
|
||||
const size = (titleSize || defaultSize) as string
|
||||
switch (size) {
|
||||
case 'sm':
|
||||
return 'text-2xl sm:text-3xl md:text-4xl'
|
||||
case 'md':
|
||||
return 'text-3xl sm:text-4xl md:text-5xl'
|
||||
case 'lg':
|
||||
return 'text-4xl sm:text-5xl md:text-6xl'
|
||||
case 'xl':
|
||||
default:
|
||||
return 'text-4xl sm:text-5xl md:text-6xl lg:text-7xl'
|
||||
}
|
||||
}
|
||||
|
||||
function HeroSection({ payload, lang }: { payload: Record<string, unknown>, lang: string }) {
|
||||
const { t } = useTranslation()
|
||||
const i18nLang = useLang()
|
||||
@@ -288,6 +317,9 @@ function HeroSection({ payload, lang }: { payload: Record<string, unknown>, lang
|
||||
const contentWidth = typeof payload.contentWidth === 'string' ? payload.contentWidth : 'standard'
|
||||
const textColorScheme = typeof payload.textColorScheme === 'string' ? payload.textColorScheme : 'light'
|
||||
const isDarkText = textColorScheme === 'dark'
|
||||
const titleSize = typeof payload.titleSize === 'string' ? payload.titleSize : 'xl'
|
||||
const titleSizeClass = getHeroTitleSizeClass(titleSize)
|
||||
const textWrapperWidthClass = titleSize === 'sm' || contentWidth === 'compact' ? 'max-w-xl' : titleSize === 'md' ? 'max-w-2xl' : 'max-w-3xl'
|
||||
const focalPoint = typeof payload.focalPoint === 'object' && payload.focalPoint ? (payload.focalPoint as { x: string, y: string }) : undefined
|
||||
|
||||
// If fields are explicitly defined as empty strings, respect them as empty
|
||||
@@ -357,14 +389,14 @@ function HeroSection({ payload, lang }: { payload: Record<string, unknown>, lang
|
||||
{renderOverlay()}
|
||||
</div>
|
||||
<div className={`relative z-10 min-h-[85vh] sm:min-h-[90vh] md:min-h-full flex flex-col ${containerClasses} ${wrapperClasses}`}>
|
||||
<div className={`w-full max-w-3xl flex flex-col space-y-5 ${contentClasses}`}>
|
||||
<div className={`w-full ${textWrapperWidthClass} flex flex-col space-y-5 ${contentClasses}`}>
|
||||
{tagline && (
|
||||
<p className={`text-xs font-semibold uppercase tracking-[0.3em] drop-shadow-md md:text-sm ${isDarkText ? 'text-brand-primary' : 'text-brand-accent'}`}>
|
||||
{tagline}
|
||||
</p>
|
||||
)}
|
||||
{title && (
|
||||
<h1 className={`ui-hero-title ${isDarkText ? '!text-text-main' : '!text-white'} drop-shadow-lg`}>
|
||||
<h1 className={`ui-hero-title ${titleSizeClass} ${isDarkText ? '!text-text-main' : '!text-white'} drop-shadow-lg`}>
|
||||
{renderAccentTitle(title, isDarkText ? 'text-brand-primary font-medium' : 'ui-accent-text font-medium')}
|
||||
</h1>
|
||||
)}
|
||||
@@ -607,14 +639,14 @@ function HeroVideoSection({ payload, lang }: { payload: Record<string, unknown>,
|
||||
<div className={`absolute inset-0 z-10 transition-opacity ${isDarkText ? 'bg-white' : 'bg-black'}`} style={{ opacity: overlayOpacity / 100 }}></div>
|
||||
|
||||
<div className={`relative z-20 py-16 h-full flex flex-col ${containerClasses} ${wrapperClasses}`}>
|
||||
<div className={`flex flex-col space-y-6 max-w-3xl ${contentClasses}`}>
|
||||
<div className={`flex flex-col space-y-6 ${payload.titleSize === 'sm' || contentWidth === 'compact' ? 'max-w-xl' : payload.titleSize === 'md' ? 'max-w-2xl' : 'max-w-3xl'} ${contentClasses}`}>
|
||||
{tagline && (
|
||||
<p className={`text-xs md:text-sm font-semibold uppercase tracking-[0.35em] drop-shadow-md ${isDarkText ? 'text-brand-primary' : 'text-brand-accent'}`}>
|
||||
{tagline}
|
||||
</p>
|
||||
)}
|
||||
{title && (
|
||||
<h1 className={`font-serif text-4xl md:text-6xl lg:text-7xl font-light leading-tight tracking-wide drop-shadow-lg ${isDarkText ? 'text-text-main' : 'text-white'}`}>
|
||||
<h1 className={`font-serif ${getTailwindHeroTitleClass(payload.titleSize, 'xl')} font-light leading-tight tracking-wide drop-shadow-lg ${isDarkText ? 'text-text-main' : 'text-white'}`}>
|
||||
{renderAccentTitle(title, isDarkText ? 'text-brand-primary font-medium' : undefined)}
|
||||
</h1>
|
||||
)}
|
||||
@@ -1012,7 +1044,7 @@ function HeroV2Section({ payload, lang }: { payload: Record<string, unknown>, la
|
||||
</div>
|
||||
)}
|
||||
|
||||
<h1 className="text-4xl md:text-5xl lg:text-6xl font-serif text-text-main leading-tight">
|
||||
<h1 className={`${getTailwindHeroTitleClass(payload.titleSize, 'lg')} font-serif text-text-main leading-tight`}>
|
||||
{renderAccentTitle(title || '', 'text-brand-primary')}
|
||||
</h1>
|
||||
|
||||
@@ -1271,8 +1303,12 @@ function BannerSection({ payload, lang }: { payload: Record<string, unknown>, la
|
||||
style={{ objectPosition: focalPoint ? `${focalPoint.x} ${focalPoint.y}` : undefined }}
|
||||
/>
|
||||
<div className={isDarkText ? "absolute inset-0 bg-white/40 backdrop-blur-[1px]" : "ui-banner-overlay"} />
|
||||
<div className={`ui-banner-content flex flex-col space-y-4 max-w-2xl ${bContent}`}>
|
||||
{title && <h2 className={`text-3xl font-semibold drop-shadow-md md:text-5xl ${isDarkText ? '!text-text-main' : 'text-brand-primary'}`}>{title}</h2>}
|
||||
<div className={`ui-banner-content flex flex-col space-y-4 ${payload.titleSize === 'sm' || contentWidth === 'compact' ? 'max-w-xl' : 'max-w-2xl'} ${bContent}`}>
|
||||
{title && (
|
||||
<h2 className={`${payload.titleSize === 'sm' ? 'text-2xl md:text-3xl' : payload.titleSize === 'md' ? 'text-2xl md:text-4xl' : payload.titleSize === 'lg' ? 'text-3xl md:text-5xl' : 'text-3xl md:text-5xl font-semibold'} drop-shadow-md ${isDarkText ? '!text-text-main' : 'text-brand-primary'}`}>
|
||||
{title}
|
||||
</h2>
|
||||
)}
|
||||
{description && <p className={`drop-shadow-sm ${isDarkText ? '!text-text-main/80' : 'text-surface-page/90'}`}>{description}</p>}
|
||||
{buttonText && (
|
||||
<div className={`flex ${bActions}`}>
|
||||
@@ -2180,7 +2216,7 @@ function HeroArticleSection({ payload, lang }: { payload: Record<string, unknown
|
||||
)}
|
||||
<div className="relative z-10 mx-auto max-w-4xl space-y-6">
|
||||
{category && <p className="text-xs font-bold uppercase tracking-[0.28em] text-brand-accent">{category}</p>}
|
||||
<h1 className="ui-hero-title">{title}</h1>
|
||||
<h1 className={`ui-hero-title ${getHeroTitleSizeClass(payload.titleSize)}`}>{title}</h1>
|
||||
{subtitle && <p className="mx-auto max-w-2xl text-lg text-surface-card md:text-xl">{subtitle}</p>}
|
||||
{(author || date) && (
|
||||
<p className="text-xs font-bold uppercase tracking-[0.22em] text-surface-card">
|
||||
|
||||
Reference in new issue
Block a user