x
This commit is contained in:
1 parent
943fd8f1a3
commit
9237c0eded
7 files changed
+776
-377
No files matched your search
@@ -14,7 +14,7 @@ import {
|
||||
} from 'lucide-react'
|
||||
|
||||
interface ThemeLivePreviewProps {
|
||||
tokens: Record<string, string>
|
||||
tokens: Record<string, any>
|
||||
}
|
||||
|
||||
// ── WCAG Relative Luminance & Contrast Calculation ──────────────────────────
|
||||
@@ -401,39 +401,45 @@ export default function ThemeLivePreview({ tokens }: ThemeLivePreviewProps) {
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-2">
|
||||
<button
|
||||
type="button"
|
||||
style={{ color: 'var(--p-text-main)' }}
|
||||
className="p-1 rounded-full hover:bg-black/5 transition"
|
||||
title="Search"
|
||||
>
|
||||
<Search className="w-3.5 h-3.5" />
|
||||
</button>
|
||||
|
||||
<div className="relative">
|
||||
{tokens.headerShowSearch !== false && (
|
||||
<button
|
||||
type="button"
|
||||
style={{ color: 'var(--p-text-main)' }}
|
||||
className="p-1 rounded-full hover:bg-black/5 transition"
|
||||
title="Cart"
|
||||
title="Search"
|
||||
>
|
||||
<ShoppingCart className="w-3.5 h-3.5" />
|
||||
<Search className="w-3.5 h-3.5" />
|
||||
</button>
|
||||
<span
|
||||
style={{ backgroundColor: 'var(--p-brand-accent)', color: '#FFFFFF' }}
|
||||
className="absolute -top-1 -right-1 h-3.5 w-3.5 rounded-full text-[8px] font-bold flex items-center justify-center shadow-sm"
|
||||
>
|
||||
2
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<button
|
||||
type="button"
|
||||
className="preview-btn-line px-2.5 py-1 text-[10px] font-bold flex items-center gap-1 cursor-pointer"
|
||||
>
|
||||
<MessageCircle className="w-3 h-3" />
|
||||
<span>LINE</span>
|
||||
</button>
|
||||
{tokens.headerShowCart !== false && (
|
||||
<div className="relative">
|
||||
<button
|
||||
type="button"
|
||||
style={{ color: 'var(--p-text-main)' }}
|
||||
className="p-1 rounded-full hover:bg-black/5 transition"
|
||||
title="Cart"
|
||||
>
|
||||
<ShoppingCart className="w-3.5 h-3.5" />
|
||||
</button>
|
||||
<span
|
||||
style={{ backgroundColor: 'var(--p-brand-accent)', color: '#FFFFFF' }}
|
||||
className="absolute -top-1 -right-1 h-3.5 w-3.5 rounded-full text-[8px] font-bold flex items-center justify-center shadow-sm"
|
||||
>
|
||||
2
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{tokens.headerShowAccount !== false && (
|
||||
<button
|
||||
type="button"
|
||||
className="preview-btn-line px-2.5 py-1 text-[10px] font-bold flex items-center gap-1 cursor-pointer"
|
||||
>
|
||||
<MessageCircle className="w-3 h-3" />
|
||||
<span>LINE</span>
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</header>
|
||||
|
||||
|
||||
@@ -19,7 +19,13 @@ import {
|
||||
ArrowUpDown,
|
||||
Monitor,
|
||||
Smartphone,
|
||||
LayoutGrid
|
||||
LayoutGrid,
|
||||
Search,
|
||||
Heart,
|
||||
ShoppingBag,
|
||||
Truck,
|
||||
Globe,
|
||||
User
|
||||
} from 'lucide-react'
|
||||
|
||||
const defaultThemeTokens = {
|
||||
@@ -169,6 +175,12 @@ export default function ThemeSettingsPage() {
|
||||
const [mobileHeaderStyle, setMobileHeaderStyle] = useState<'logo_center' | 'logo_left' | 'search_bar_below'>('logo_center')
|
||||
const [mobileHeaderBehavior, setMobileHeaderBehavior] = useState<'always_sticky' | 'hide_on_scroll'>('hide_on_scroll')
|
||||
const [cardPreset, setCardPreset] = useState<'standard' | 'minimal' | 'swatch'>('standard')
|
||||
const [headerShowSearch, setHeaderShowSearch] = useState<boolean>(true)
|
||||
const [headerShowWishlist, setHeaderShowWishlist] = useState<boolean>(true)
|
||||
const [headerShowCart, setHeaderShowCart] = useState<boolean>(true)
|
||||
const [headerShowTrackOrder, setHeaderShowTrackOrder] = useState<boolean>(true)
|
||||
const [headerShowLanguage, setHeaderShowLanguage] = useState<boolean>(true)
|
||||
const [headerShowAccount, setHeaderShowAccount] = useState<boolean>(true)
|
||||
|
||||
// JSON Quick Copy / Paste State
|
||||
const [isJsonBoxOpen, setIsJsonBoxOpen] = useState(false)
|
||||
@@ -201,6 +213,12 @@ export default function ThemeSettingsPage() {
|
||||
setMobileHeaderBehavior((data as any).mobileHeaderBehavior || (data.themeTokens as any)?.mobileHeaderBehavior || pcBehavior)
|
||||
const cp = (data as any).cardPreset || (data.themeTokens as any)?.cardPreset || 'standard'
|
||||
setCardPreset(cp === 'minimal_swatch' ? 'swatch' : cp)
|
||||
setHeaderShowSearch((data as any).headerShowSearch ?? (data.themeTokens as any)?.headerShowSearch ?? true)
|
||||
setHeaderShowWishlist((data as any).headerShowWishlist ?? (data.themeTokens as any)?.headerShowWishlist ?? true)
|
||||
setHeaderShowCart((data as any).headerShowCart ?? (data.themeTokens as any)?.headerShowCart ?? true)
|
||||
setHeaderShowTrackOrder((data as any).headerShowTrackOrder ?? (data.themeTokens as any)?.headerShowTrackOrder ?? true)
|
||||
setHeaderShowLanguage((data as any).headerShowLanguage ?? (data.themeTokens as any)?.headerShowLanguage ?? true)
|
||||
setHeaderShowAccount((data as any).headerShowAccount ?? (data.themeTokens as any)?.headerShowAccount ?? true)
|
||||
})
|
||||
.catch(console.error)
|
||||
.finally(() => setLoading(false))
|
||||
@@ -221,6 +239,12 @@ export default function ThemeSettingsPage() {
|
||||
headerBehavior,
|
||||
mobileHeaderBehavior,
|
||||
mobileHeaderStyle,
|
||||
headerShowSearch,
|
||||
headerShowWishlist,
|
||||
headerShowCart,
|
||||
headerShowTrackOrder,
|
||||
headerShowLanguage,
|
||||
headerShowAccount,
|
||||
cardPreset,
|
||||
},
|
||||
cardPreset,
|
||||
@@ -231,6 +255,12 @@ export default function ThemeSettingsPage() {
|
||||
headerBehavior,
|
||||
mobileHeaderStyle,
|
||||
mobileHeaderBehavior,
|
||||
headerShowSearch,
|
||||
headerShowWishlist,
|
||||
headerShowCart,
|
||||
headerShowTrackOrder,
|
||||
headerShowLanguage,
|
||||
headerShowAccount,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -325,6 +355,25 @@ export default function ThemeSettingsPage() {
|
||||
} else if (extractedTokens?.mobileHeaderBehavior === 'always_sticky' || extractedTokens?.mobileHeaderBehavior === 'hide_on_scroll') {
|
||||
setMobileHeaderBehavior(extractedTokens.mobileHeaderBehavior)
|
||||
}
|
||||
|
||||
// Extract Header Action Icons Visibility
|
||||
if (typeof parsed.headerShowSearch === 'boolean') setHeaderShowSearch(parsed.headerShowSearch)
|
||||
else if (typeof extractedTokens?.headerShowSearch === 'boolean') setHeaderShowSearch(extractedTokens.headerShowSearch)
|
||||
|
||||
if (typeof parsed.headerShowWishlist === 'boolean') setHeaderShowWishlist(parsed.headerShowWishlist)
|
||||
else if (typeof extractedTokens?.headerShowWishlist === 'boolean') setHeaderShowWishlist(extractedTokens.headerShowWishlist)
|
||||
|
||||
if (typeof parsed.headerShowCart === 'boolean') setHeaderShowCart(parsed.headerShowCart)
|
||||
else if (typeof extractedTokens?.headerShowCart === 'boolean') setHeaderShowCart(extractedTokens.headerShowCart)
|
||||
|
||||
if (typeof parsed.headerShowTrackOrder === 'boolean') setHeaderShowTrackOrder(parsed.headerShowTrackOrder)
|
||||
else if (typeof extractedTokens?.headerShowTrackOrder === 'boolean') setHeaderShowTrackOrder(extractedTokens.headerShowTrackOrder)
|
||||
|
||||
if (typeof parsed.headerShowLanguage === 'boolean') setHeaderShowLanguage(parsed.headerShowLanguage)
|
||||
else if (typeof extractedTokens?.headerShowLanguage === 'boolean') setHeaderShowLanguage(extractedTokens.headerShowLanguage)
|
||||
|
||||
if (typeof parsed.headerShowAccount === 'boolean') setHeaderShowAccount(parsed.headerShowAccount)
|
||||
else if (typeof extractedTokens?.headerShowAccount === 'boolean') setHeaderShowAccount(extractedTokens.headerShowAccount)
|
||||
}
|
||||
|
||||
// ── Paste JSON Text Handlers ──────────────────────────────────────────────
|
||||
@@ -450,6 +499,12 @@ export default function ThemeSettingsPage() {
|
||||
headerBehavior,
|
||||
mobileHeaderBehavior,
|
||||
mobileHeaderStyle,
|
||||
headerShowSearch,
|
||||
headerShowWishlist,
|
||||
headerShowCart,
|
||||
headerShowTrackOrder,
|
||||
headerShowLanguage,
|
||||
headerShowAccount,
|
||||
cardPreset,
|
||||
},
|
||||
cardPreset,
|
||||
@@ -460,6 +515,12 @@ export default function ThemeSettingsPage() {
|
||||
headerBehavior,
|
||||
mobileHeaderStyle,
|
||||
mobileHeaderBehavior,
|
||||
headerShowSearch,
|
||||
headerShowWishlist,
|
||||
headerShowCart,
|
||||
headerShowTrackOrder,
|
||||
headerShowLanguage,
|
||||
headerShowAccount,
|
||||
}),
|
||||
})
|
||||
if (res.ok) {
|
||||
@@ -1262,6 +1323,141 @@ export default function ThemeSettingsPage() {
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Header Action Icons (Corner Icons) */}
|
||||
<div className="space-y-4 pt-6 border-t border-white/5">
|
||||
<div>
|
||||
<label className="text-xs uppercase font-bold tracking-widest text-slate-400 flex items-center gap-1.5">
|
||||
<ShoppingBag className="w-3.5 h-3.5 text-amber-400" />
|
||||
<span>Header Action Icons (ไอคอนมุมขวาบนของ Header)</span>
|
||||
</label>
|
||||
<p className="text-xs text-slate-400 mt-1">
|
||||
กำหนดเปิด/ซ่อนไอคอนการทำงานที่มุมขวาบนของ Header (ทั้งบน PC และ Mobile) เหมาะสำหรับร้านที่ไม่มีระบบตะกร้าสินค้า หรือต้องการซ่อนบางฟังก์ชัน
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="grid gap-3 sm:grid-cols-2 lg:grid-cols-3">
|
||||
{/* Cart */}
|
||||
<div className={`flex items-start justify-between p-4 rounded-xl border transition-all ${headerShowCart ? 'bg-white/5 border-amber-500/40' : 'bg-night-950/40 border-white/5 opacity-70'}`}>
|
||||
<div className="flex items-start gap-3 min-w-0">
|
||||
<div className={`p-2 rounded-lg shrink-0 ${headerShowCart ? 'bg-amber-400/20 text-amber-300' : 'bg-white/5 text-slate-500'}`}>
|
||||
<ShoppingBag className="w-4 h-4" />
|
||||
</div>
|
||||
<div>
|
||||
<span className="text-xs font-bold text-white block">ตะกร้าสินค้า (Cart)</span>
|
||||
<span className="text-[10px] text-slate-400 block mt-0.5 leading-tight">ซ่อนปุ่มตะกร้า หากเป็นร้านโชว์สินค้า/ไม่มีระบบสั่งซื้อ</span>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setHeaderShowCart(v => !v)}
|
||||
className={`relative inline-flex h-6 w-11 shrink-0 items-center rounded-full transition-colors cursor-pointer ml-2 ${headerShowCart ? 'bg-amber-500' : 'bg-slate-700'}`}
|
||||
>
|
||||
<span className={`inline-block h-4 w-4 transform rounded-full bg-white shadow transition-transform ${headerShowCart ? 'translate-x-6' : 'translate-x-1'}`} />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Wishlist */}
|
||||
<div className={`flex items-start justify-between p-4 rounded-xl border transition-all ${headerShowWishlist ? 'bg-white/5 border-amber-500/40' : 'bg-night-950/40 border-white/5 opacity-70'}`}>
|
||||
<div className="flex items-start gap-3 min-w-0">
|
||||
<div className={`p-2 rounded-lg shrink-0 ${headerShowWishlist ? 'bg-rose-500/20 text-rose-300' : 'bg-white/5 text-slate-500'}`}>
|
||||
<Heart className="w-4 h-4" />
|
||||
</div>
|
||||
<div>
|
||||
<span className="text-xs font-bold text-white block">รายการโปรด (Wishlist)</span>
|
||||
<span className="text-[10px] text-slate-400 block mt-0.5 leading-tight">ไอคอนหัวใจบันทึกรายการโปรด</span>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setHeaderShowWishlist(v => !v)}
|
||||
className={`relative inline-flex h-6 w-11 shrink-0 items-center rounded-full transition-colors cursor-pointer ml-2 ${headerShowWishlist ? 'bg-amber-500' : 'bg-slate-700'}`}
|
||||
>
|
||||
<span className={`inline-block h-4 w-4 transform rounded-full bg-white shadow transition-transform ${headerShowWishlist ? 'translate-x-6' : 'translate-x-1'}`} />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Search */}
|
||||
<div className={`flex items-start justify-between p-4 rounded-xl border transition-all ${headerShowSearch ? 'bg-white/5 border-amber-500/40' : 'bg-night-950/40 border-white/5 opacity-70'}`}>
|
||||
<div className="flex items-start gap-3 min-w-0">
|
||||
<div className={`p-2 rounded-lg shrink-0 ${headerShowSearch ? 'bg-cyan-500/20 text-cyan-300' : 'bg-white/5 text-slate-500'}`}>
|
||||
<Search className="w-4 h-4" />
|
||||
</div>
|
||||
<div>
|
||||
<span className="text-xs font-bold text-white block">ค้นหา (Search)</span>
|
||||
<span className="text-[10px] text-slate-400 block mt-0.5 leading-tight">ปุ่มเปิดหน้าต่างค้นหาสินค้า</span>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setHeaderShowSearch(v => !v)}
|
||||
className={`relative inline-flex h-6 w-11 shrink-0 items-center rounded-full transition-colors cursor-pointer ml-2 ${headerShowSearch ? 'bg-amber-500' : 'bg-slate-700'}`}
|
||||
>
|
||||
<span className={`inline-block h-4 w-4 transform rounded-full bg-white shadow transition-transform ${headerShowSearch ? 'translate-x-6' : 'translate-x-1'}`} />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Track Order */}
|
||||
<div className={`flex items-start justify-between p-4 rounded-xl border transition-all ${headerShowTrackOrder ? 'bg-white/5 border-amber-500/40' : 'bg-night-950/40 border-white/5 opacity-70'}`}>
|
||||
<div className="flex items-start gap-3 min-w-0">
|
||||
<div className={`p-2 rounded-lg shrink-0 ${headerShowTrackOrder ? 'bg-emerald-500/20 text-emerald-300' : 'bg-white/5 text-slate-500'}`}>
|
||||
<Truck className="w-4 h-4" />
|
||||
</div>
|
||||
<div>
|
||||
<span className="text-xs font-bold text-white block">ติดตามพัสดุ (Track Order)</span>
|
||||
<span className="text-[10px] text-slate-400 block mt-0.5 leading-tight">ปุ่มตรวจสอบสถานะจัดส่งสินค้า</span>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setHeaderShowTrackOrder(v => !v)}
|
||||
className={`relative inline-flex h-6 w-11 shrink-0 items-center rounded-full transition-colors cursor-pointer ml-2 ${headerShowTrackOrder ? 'bg-amber-500' : 'bg-slate-700'}`}
|
||||
>
|
||||
<span className={`inline-block h-4 w-4 transform rounded-full bg-white shadow transition-transform ${headerShowTrackOrder ? 'translate-x-6' : 'translate-x-1'}`} />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Language Switcher */}
|
||||
<div className={`flex items-start justify-between p-4 rounded-xl border transition-all ${headerShowLanguage ? 'bg-white/5 border-amber-500/40' : 'bg-night-950/40 border-white/5 opacity-70'}`}>
|
||||
<div className="flex items-start gap-3 min-w-0">
|
||||
<div className={`p-2 rounded-lg shrink-0 ${headerShowLanguage ? 'bg-violet-500/20 text-violet-300' : 'bg-white/5 text-slate-500'}`}>
|
||||
<Globe className="w-4 h-4" />
|
||||
</div>
|
||||
<div>
|
||||
<span className="text-xs font-bold text-white block">เปลี่ยนภาษา (Language)</span>
|
||||
<span className="text-[10px] text-slate-400 block mt-0.5 leading-tight">เมนูเลือกภาษา (TH, EN, JA ฯลฯ)</span>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setHeaderShowLanguage(v => !v)}
|
||||
className={`relative inline-flex h-6 w-11 shrink-0 items-center rounded-full transition-colors cursor-pointer ml-2 ${headerShowLanguage ? 'bg-amber-500' : 'bg-slate-700'}`}
|
||||
>
|
||||
<span className={`inline-block h-4 w-4 transform rounded-full bg-white shadow transition-transform ${headerShowLanguage ? 'translate-x-6' : 'translate-x-1'}`} />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Account / Login */}
|
||||
<div className={`flex items-start justify-between p-4 rounded-xl border transition-all ${headerShowAccount ? 'bg-white/5 border-amber-500/40' : 'bg-night-950/40 border-white/5 opacity-70'}`}>
|
||||
<div className="flex items-start gap-3 min-w-0">
|
||||
<div className={`p-2 rounded-lg shrink-0 ${headerShowAccount ? 'bg-lime-500/20 text-lime-300' : 'bg-white/5 text-slate-500'}`}>
|
||||
<User className="w-4 h-4" />
|
||||
</div>
|
||||
<div>
|
||||
<span className="text-xs font-bold text-white block">บัญชี / ล็อกอิน (Account)</span>
|
||||
<span className="text-[10px] text-slate-400 block mt-0.5 leading-tight">ปุ่ม LINE Login หรือโปรไฟล์ผู้ใช้</span>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setHeaderShowAccount(v => !v)}
|
||||
className={`relative inline-flex h-6 w-11 shrink-0 items-center rounded-full transition-colors cursor-pointer ml-2 ${headerShowAccount ? 'bg-amber-500' : 'bg-slate-700'}`}
|
||||
>
|
||||
<span className={`inline-block h-4 w-4 transform rounded-full bg-white shadow transition-transform ${headerShowAccount ? 'translate-x-6' : 'translate-x-1'}`} />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* ── 5. Mobile Header Settings ── */}
|
||||
@@ -1487,7 +1683,15 @@ export default function ThemeSettingsPage() {
|
||||
|
||||
{/* ── Right Column: Live Interactive UI Preview (Sticky) ── */}
|
||||
<div className="xl:col-span-5 xl:sticky xl:top-20 space-y-6">
|
||||
<ThemeLivePreview tokens={themeTokens} />
|
||||
<ThemeLivePreview tokens={{
|
||||
...themeTokens,
|
||||
headerShowSearch,
|
||||
headerShowWishlist,
|
||||
headerShowCart,
|
||||
headerShowTrackOrder,
|
||||
headerShowLanguage,
|
||||
headerShowAccount,
|
||||
}} />
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
Reference in new issue
Block a user