feat(ui): white-label theme customizer panel — Türkçe + sansürlü
Yan paneldeki görünüm özelleştirme widget'ı template branding'inden
arındırıldı:
- 'Customizer' → 'Görünüm'
- Tab adları: 'Theme'/'Layout' → 'Renk'/'Düzen'
- 'Shadcn UI Theme Presets' → 'Hazır temalar'
- 'Tweakcn Theme Presets' → 'Genişletilmiş temalar' (kaynak adı
uygulamada görünmüyor)
- 'Random' → 'Rastgele', 'Choose ... Theme' → 'Tema seçin'
- 'Radius' → 'Köşe yuvarlama'
- 'Mode' / 'Light' / 'Dark' → 'Görünüm modu' / 'Açık' / 'Koyu'
- 'Import Theme' → 'Tema içe aktar' (modal başlığı + butonlar dahil)
- 'Brand Colors' → 'Marka renkleri'; alt etiketler de TR
- 'Sidebar Variant'/'Collapsible Mode'/'Position' → 'Kenar çubuğu stili
/ Daraltma davranışı / Kenar çubuğu konumu', açıklamalar TR
- Constants (theme-customizer-constants): name alanları Türkçe
(Default→Standart, Floating→Yüzen, Inset→İçeri çekik, Off Canvas→Gizle,
Icon→İkon, None→Sabit, Left→Sol, Right→Sağ; brand color isimleri de
Türkçeye çevrildi)
- Tweakcn.com reklam kartı **tamamen kaldırıldı** (artık 3rd party link
yok). Onun yerine import butonunun altına nötr bir ipucu yazısı:
'tweakcn.com gibi araçlardan dışa aktardığınız JSON tema dosyasını
yükleyebilirsiniz.' — yani aracı bilgi olarak veriyoruz, reklam değil.
- Reset/X butonlarına a11y label + tooltip eklendi.
Theme datasını ('colorThemes' / 'tweakcnThemes' from @/config/theme-data)
ve hook adlarını ('applyTweakcnTheme', 'tweakcn-theme-presets') aynen
bıraktım — kullanıcıya görünmüyor, refactor maliyetli, runtime davranışı
identical.
This commit is contained in:
@@ -66,9 +66,10 @@ export function ImportModal({ open, onOpenChange, onImport }: ImportModalProps)
|
||||
<Dialog open={open} onOpenChange={onOpenChange} modal={true}>
|
||||
<DialogContent className="max-w-4xl w-[90vw]">
|
||||
<DialogHeader>
|
||||
<DialogTitle>Import Custom CSS</DialogTitle>
|
||||
<DialogTitle>Tema CSS'i içe aktar</DialogTitle>
|
||||
<DialogDescription>
|
||||
Paste your CSS theme below. Include both <code>:root</code> (light mode) and <code>.dark</code> (dark mode) sections with CSS variables like <code>--primary</code>, <code>--background</code>, etc. The theme will automatically switch between light and dark modes.
|
||||
CSS temasını aşağıya yapıştırın. Açık tema için <code>:root</code> ve koyu tema için{" "}
|
||||
<code>.dark</code> bölümleri olmalı; <code>--primary</code>, <code>--background</code> gibi CSS değişkenlerini içerir. Açık/koyu mod arasında otomatik geçiş yapılır.
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<div className="space-y-4">
|
||||
@@ -94,10 +95,10 @@ export function ImportModal({ open, onOpenChange, onImport }: ImportModalProps)
|
||||
</div>
|
||||
<div className="flex gap-2 justify-end">
|
||||
<Button variant="outline" onClick={() => onOpenChange(false)} className="cursor-pointer">
|
||||
Cancel
|
||||
Vazgeç
|
||||
</Button>
|
||||
<Button onClick={processImport} disabled={!importText.trim()} className="cursor-pointer">
|
||||
Import Theme
|
||||
İçe aktar
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -96,18 +96,31 @@ export function ThemeCustomizer({ open, onOpenChange }: ThemeCustomizerProps) {
|
||||
<div className="p-2 bg-primary/10 rounded-lg">
|
||||
<Settings className="h-4 w-4" />
|
||||
</div>
|
||||
<SheetTitle className="text-lg font-semibold">Customizer</SheetTitle>
|
||||
<SheetTitle className="text-lg font-semibold">Görünüm</SheetTitle>
|
||||
<div className="ml-auto flex items-center gap-2">
|
||||
<Button variant="outline" size="icon" onClick={handleReset} className="cursor-pointer h-8 w-8">
|
||||
<Button
|
||||
variant="outline"
|
||||
size="icon"
|
||||
onClick={handleReset}
|
||||
className="cursor-pointer h-8 w-8"
|
||||
aria-label="Varsayılana dön"
|
||||
title="Varsayılana dön"
|
||||
>
|
||||
<RotateCcw className="h-4 w-4" />
|
||||
</Button>
|
||||
<Button variant="outline" size="icon" onClick={() => onOpenChange(false)} className="cursor-pointer h-8 w-8">
|
||||
<Button
|
||||
variant="outline"
|
||||
size="icon"
|
||||
onClick={() => onOpenChange(false)}
|
||||
className="cursor-pointer h-8 w-8"
|
||||
aria-label="Kapat"
|
||||
>
|
||||
<X className="h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<SheetDescription className="text-sm text-muted-foreground sr-only">
|
||||
Customize the them and layout of your dashboard.
|
||||
Panelin renklerini ve düzenini özelleştirin.
|
||||
</SheetDescription>
|
||||
</SheetHeader>
|
||||
|
||||
@@ -115,13 +128,13 @@ export function ThemeCustomizer({ open, onOpenChange }: ThemeCustomizerProps) {
|
||||
<Tabs value={activeTab} onValueChange={setActiveTab} className="h-full flex flex-col">
|
||||
<div className="py-2">
|
||||
<TabsList className="grid w-full grid-cols-2 rounded-none h-12 p-1.5">
|
||||
<TabsTrigger value="theme" className="cursor-pointer data-[state=active]:bg-background"><Palette className="h-4 w-4 mr-1" /> Theme</TabsTrigger>
|
||||
<TabsTrigger value="layout" className="cursor-pointer data-[state=active]:bg-background"><Layout className="h-4 w-4 mr-1" /> Layout</TabsTrigger>
|
||||
<TabsTrigger value="theme" className="cursor-pointer data-[state=active]:bg-background">
|
||||
<Palette className="h-4 w-4 mr-1" /> Renk
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="layout" className="cursor-pointer data-[state=active]:bg-background">
|
||||
<Layout className="h-4 w-4 mr-1" /> Düzen
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
{/* <TabsList className="grid w-full grid-cols-2 rounded-none h-12 p-1.5">
|
||||
<TabsTrigger value="theme" className="cursor-pointer data-[state=active]:bg-primary data-[state=active]:text-primary-foreground"><Palette className="h-4 w-4 mr-1" /> Theme</TabsTrigger>
|
||||
<TabsTrigger value="layout" className="cursor-pointer data-[state=active]:bg-primary data-[state=active]:text-primary-foreground"><Layout className="h-4 w-4 mr-1" /> Layout</TabsTrigger>
|
||||
</TabsList> */}
|
||||
</div>
|
||||
|
||||
<TabsContent value="theme" className="flex-1 mt-0">
|
||||
|
||||
@@ -30,16 +30,15 @@ export function LayoutTab() {
|
||||
|
||||
return (
|
||||
<div className="p-4 space-y-6">
|
||||
{/* Sidebar Configuration */}
|
||||
{/* Kenar çubuğu ayarları */}
|
||||
<div className="space-y-3">
|
||||
{/* Sidebar Variant */}
|
||||
<div>
|
||||
<Label className="text-sm font-medium">Sidebar Variant</Label>
|
||||
<Label className="text-sm font-medium">Kenar çubuğu stili</Label>
|
||||
{sidebarConfig.variant && (
|
||||
<p className="text-xs text-muted-foreground mt-1">
|
||||
{sidebarConfig.variant === "sidebar" && "Default: Standard sidebar layout"}
|
||||
{sidebarConfig.variant === "floating" && "Floating: Floating sidebar with border"}
|
||||
{sidebarConfig.variant === "inset" && "Inset: Inset sidebar with rounded corners"}
|
||||
{sidebarConfig.variant === "sidebar" && "Standart: klasik kenar çubuğu"}
|
||||
{sidebarConfig.variant === "floating" && "Yüzen: çerçeveli, yüzen kenar çubuğu"}
|
||||
{sidebarConfig.variant === "inset" && "İçeri çekik: yuvarlak köşeli içeri çekik"}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
@@ -84,15 +83,15 @@ export function LayoutTab() {
|
||||
|
||||
<Separator />
|
||||
|
||||
{/* Sidebar Collapsible Mode */}
|
||||
{/* Daraltma davranışı */}
|
||||
<div className="space-y-3">
|
||||
<div>
|
||||
<Label className="text-sm font-medium">Sidebar Collapsible Mode</Label>
|
||||
<Label className="text-sm font-medium">Daraltma davranışı</Label>
|
||||
{sidebarConfig.collapsible && (
|
||||
<p className="text-xs text-muted-foreground mt-1">
|
||||
{sidebarConfig.collapsible === "offcanvas" && "Off Canvas: Slides out of view"}
|
||||
{sidebarConfig.collapsible === "icon" && "Icon: Collapses to icon only"}
|
||||
{sidebarConfig.collapsible === "none" && "None: Always visible"}
|
||||
{sidebarConfig.collapsible === "offcanvas" && "Gizle: kenar görünümden tamamen kayar"}
|
||||
{sidebarConfig.collapsible === "icon" && "İkon: sadece ikonlar görünür"}
|
||||
{sidebarConfig.collapsible === "none" && "Sabit: her zaman açık"}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
@@ -152,14 +151,14 @@ export function LayoutTab() {
|
||||
|
||||
<Separator />
|
||||
|
||||
{/* Sidebar Side */}
|
||||
{/* Kenar çubuğu konumu */}
|
||||
<div className="space-y-3">
|
||||
<div>
|
||||
<Label className="text-sm font-medium">Sidebar Position</Label>
|
||||
<Label className="text-sm font-medium">Kenar çubuğu konumu</Label>
|
||||
{sidebarConfig.side && (
|
||||
<p className="text-xs text-muted-foreground mt-1">
|
||||
{sidebarConfig.side === "left" && "Left: Sidebar positioned on the left side"}
|
||||
{sidebarConfig.side === "right" && "Right: Sidebar positioned on the right side"}
|
||||
{sidebarConfig.side === "left" && "Sol: kenar çubuğu solda"}
|
||||
{sidebarConfig.side === "right" && "Sağ: kenar çubuğu sağda"}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use client"
|
||||
|
||||
import { Palette, Dices, Upload, ExternalLink, Sun, Moon } from 'lucide-react'
|
||||
import { Dices, Upload, Sun, Moon } from 'lucide-react'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Label } from '@/components/ui/label'
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'
|
||||
@@ -87,25 +87,25 @@ export function ThemeTab({
|
||||
<div className="p-4 space-y-6">
|
||||
|
||||
|
||||
{/* Shadcn UI Theme Presets */}
|
||||
{/* Hazır temalar */}
|
||||
<div className="space-y-3">
|
||||
<div className="flex items-center justify-between">
|
||||
<Label className="text-sm font-medium">Shadcn UI Theme Presets</Label>
|
||||
<Label className="text-sm font-medium">Hazır temalar</Label>
|
||||
<Button variant="outline" size="sm" onClick={handleRandomShadcn} className="cursor-pointer">
|
||||
<Dices className="h-3.5 w-3.5 mr-1.5" />
|
||||
Random
|
||||
Rastgele
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<Select value={selectedTheme} onValueChange={(value) => {
|
||||
setSelectedTheme(value)
|
||||
setSelectedTweakcnTheme("") // Clear tweakcn selection
|
||||
setSelectedTweakcnTheme("") // Clear other selection
|
||||
setBrandColorsValues({}) // Clear brand colors state
|
||||
setImportedTheme(null) // Clear imported theme
|
||||
applyTheme(value, isDarkMode)
|
||||
}}>
|
||||
<SelectTrigger className="w-full cursor-pointer">
|
||||
<SelectValue placeholder="Choose Shadcn Theme" />
|
||||
<SelectValue placeholder="Tema seçin" />
|
||||
</SelectTrigger>
|
||||
<SelectContent className="max-h-60">
|
||||
<div className="p-2">
|
||||
@@ -141,19 +141,19 @@ export function ThemeTab({
|
||||
|
||||
<Separator />
|
||||
|
||||
{/* Tweakcn Theme Presets */}
|
||||
{/* Genişletilmiş temalar */}
|
||||
<div className="space-y-3">
|
||||
<div className="flex items-center justify-between">
|
||||
<Label className="text-sm font-medium">Tweakcn Theme Presets</Label>
|
||||
<Label className="text-sm font-medium">Genişletilmiş temalar</Label>
|
||||
<Button variant="outline" size="sm" onClick={handleRandomTweakcn} className="cursor-pointer">
|
||||
<Dices className="h-3.5 w-3.5 mr-1.5" />
|
||||
Random
|
||||
Rastgele
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<Select value={selectedTweakcnTheme} onValueChange={(value) => {
|
||||
setSelectedTweakcnTheme(value)
|
||||
setSelectedTheme("") // Clear shadcn selection
|
||||
setSelectedTheme("") // Clear other selection
|
||||
setBrandColorsValues({}) // Clear brand colors state
|
||||
setImportedTheme(null) // Clear imported theme
|
||||
const selectedPreset = tweakcnThemes.find(t => t.value === value)?.preset
|
||||
@@ -162,7 +162,7 @@ export function ThemeTab({
|
||||
}
|
||||
}}>
|
||||
<SelectTrigger className="w-full cursor-pointer">
|
||||
<SelectValue placeholder="Choose Tweakcn Theme" />
|
||||
<SelectValue placeholder="Tema seçin" />
|
||||
</SelectTrigger>
|
||||
<SelectContent className="max-h-60">
|
||||
<div className="p-2">
|
||||
@@ -198,9 +198,9 @@ export function ThemeTab({
|
||||
|
||||
<Separator />
|
||||
|
||||
{/* Radius Selection */}
|
||||
{/* Köşe yuvarlama */}
|
||||
<div className="space-y-3">
|
||||
<Label className="text-sm font-medium">Radius</Label>
|
||||
<Label className="text-sm font-medium">Köşe yuvarlama</Label>
|
||||
<div className="grid grid-cols-5 gap-2">
|
||||
{radiusOptions.map((option) => (
|
||||
<div
|
||||
@@ -222,9 +222,9 @@ export function ThemeTab({
|
||||
|
||||
<Separator />
|
||||
|
||||
{/* Mode Section */}
|
||||
{/* Görünüm modu */}
|
||||
<div className="space-y-3">
|
||||
<Label className="text-sm font-medium">Mode</Label>
|
||||
<Label className="text-sm font-medium">Görünüm modu</Label>
|
||||
<div className="grid grid-cols-2 gap-2">
|
||||
<Button
|
||||
variant={!isDarkMode ? "secondary" : "outline"}
|
||||
@@ -233,7 +233,7 @@ export function ThemeTab({
|
||||
className="cursor-pointer"
|
||||
>
|
||||
<Sun className="h-4 w-4 mr-1" />
|
||||
Light
|
||||
Açık
|
||||
</Button>
|
||||
<Button
|
||||
variant={isDarkMode ? "secondary" : "outline"}
|
||||
@@ -242,14 +242,14 @@ export function ThemeTab({
|
||||
className="cursor-pointer"
|
||||
>
|
||||
<Moon className="h-4 w-4 mr-1" />
|
||||
Dark
|
||||
Koyu
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Separator />
|
||||
|
||||
{/* Import Theme Button */}
|
||||
{/* Tema içe aktar */}
|
||||
<div className="space-y-3">
|
||||
<Button
|
||||
variant="outline"
|
||||
@@ -258,15 +258,18 @@ export function ThemeTab({
|
||||
className="w-full cursor-pointer"
|
||||
>
|
||||
<Upload className="h-3.5 w-3.5 mr-1.5" />
|
||||
Import Theme
|
||||
Tema içe aktar
|
||||
</Button>
|
||||
<p className="text-muted-foreground text-xs">
|
||||
tweakcn.com gibi araçlardan dışa aktardığınız JSON tema dosyasını yükleyebilirsiniz.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Brand Colors Section */}
|
||||
{/* Marka renkleri */}
|
||||
<Accordion type="single" collapsible className="w-full border-b rounded-lg">
|
||||
<AccordionItem value="brand-colors" className="border border-border rounded-lg overflow-hidden">
|
||||
<AccordionTrigger className="px-4 py-3 hover:no-underline hover:bg-muted/50 transition-colors">
|
||||
<Label className="text-sm font-medium cursor-pointer">Brand Colors</Label>
|
||||
<Label className="text-sm font-medium cursor-pointer">Marka renkleri</Label>
|
||||
</AccordionTrigger>
|
||||
<AccordionContent className="px-4 pb-4 pt-2 space-y-3 border-t border-border bg-muted/20">
|
||||
{baseColors.map((color) => (
|
||||
@@ -282,34 +285,6 @@ export function ThemeTab({
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
</Accordion>
|
||||
|
||||
{/* Tweakcn */}
|
||||
<div className="p-4 bg-muted rounded-lg space-y-3">
|
||||
<div className="flex items-center gap-2">
|
||||
<Palette className="h-4 w-4 text-primary" />
|
||||
<span className="text-sm font-medium">Advanced Customization</span>
|
||||
</div>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
For advanced theme customization with real-time preview, visual color picker, and hundreds of prebuilt themes, visit{" "}
|
||||
<a
|
||||
href="https://tweakcn.com/editor/theme"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-primary hover:underline font-medium cursor-pointer"
|
||||
>
|
||||
tweakcn.com
|
||||
</a>
|
||||
</p>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="w-full cursor-pointer"
|
||||
onClick={() => typeof window !== "undefined" && window.open('https://tweakcn.com/editor/theme', '_blank')}
|
||||
>
|
||||
<ExternalLink className="h-3.5 w-3.5 mr-1.5" />
|
||||
Open Tweakcn
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
import type {
|
||||
SidebarVariant,
|
||||
SidebarCollapsibleOption,
|
||||
SidebarSideOption,
|
||||
RadiusOption,
|
||||
BrandColor
|
||||
} from '@/types/theme-customizer'
|
||||
import type {
|
||||
SidebarVariant,
|
||||
SidebarCollapsibleOption,
|
||||
SidebarSideOption,
|
||||
RadiusOption,
|
||||
BrandColor,
|
||||
} from "@/types/theme-customizer"
|
||||
|
||||
// Radius options
|
||||
export const radiusOptions: RadiusOption[] = [
|
||||
{ name: "0", value: "0rem" },
|
||||
{ name: "0.3", value: "0.3rem" },
|
||||
@@ -15,34 +14,30 @@ export const radiusOptions: RadiusOption[] = [
|
||||
{ name: "1.0", value: "1rem" },
|
||||
]
|
||||
|
||||
// Sidebar variant options
|
||||
export const sidebarVariants: SidebarVariant[] = [
|
||||
{ name: "Default", value: "sidebar", description: "Standard sidebar layout" },
|
||||
{ name: "Floating", value: "floating", description: "Floating sidebar with border" },
|
||||
{ name: "Inset", value: "inset", description: "Inset sidebar with rounded corners" },
|
||||
{ name: "Standart", value: "sidebar", description: "Klasik kenar çubuğu" },
|
||||
{ name: "Yüzen", value: "floating", description: "Çerçeveli, yüzen kenar çubuğu" },
|
||||
{ name: "İçeri çekik", value: "inset", description: "Yuvarlak köşeli içeri çekik kenar" },
|
||||
]
|
||||
|
||||
// Sidebar collapsible options
|
||||
export const sidebarCollapsibleOptions: SidebarCollapsibleOption[] = [
|
||||
{ name: "Off Canvas", value: "offcanvas", description: "Slides out of view" },
|
||||
{ name: "Icon", value: "icon", description: "Collapses to icon only" },
|
||||
{ name: "None", value: "none", description: "Always visible" },
|
||||
{ name: "Gizle", value: "offcanvas", description: "Görünümden tamamen kayar" },
|
||||
{ name: "İkon", value: "icon", description: "Sadece ikonlar görünür" },
|
||||
{ name: "Sabit", value: "none", description: "Her zaman açık" },
|
||||
]
|
||||
|
||||
// Sidebar side options
|
||||
export const sidebarSideOptions: SidebarSideOption[] = [
|
||||
{ name: "Left", value: "left" },
|
||||
{ name: "Right", value: "right" },
|
||||
{ name: "Sol", value: "left" },
|
||||
{ name: "Sağ", value: "right" },
|
||||
]
|
||||
|
||||
// Define brand colors for custom color inputs
|
||||
export const baseColors: BrandColor[] = [
|
||||
{ name: "Primary", cssVar: "--primary" },
|
||||
{ name: "Primary Foreground", cssVar: "--primary-foreground" },
|
||||
{ name: "Secondary", cssVar: "--secondary" },
|
||||
{ name: "Secondary Foreground", cssVar: "--secondary-foreground" },
|
||||
{ name: "Accent", cssVar: "--accent" },
|
||||
{ name: "Accent Foreground", cssVar: "--accent-foreground" },
|
||||
{ name: "Muted", cssVar: "--muted" },
|
||||
{ name: "Muted Foreground", cssVar: "--muted-foreground" },
|
||||
{ name: "Birincil", cssVar: "--primary" },
|
||||
{ name: "Birincil — yazı", cssVar: "--primary-foreground" },
|
||||
{ name: "İkincil", cssVar: "--secondary" },
|
||||
{ name: "İkincil — yazı", cssVar: "--secondary-foreground" },
|
||||
{ name: "Vurgu", cssVar: "--accent" },
|
||||
{ name: "Vurgu — yazı", cssVar: "--accent-foreground" },
|
||||
{ name: "Soluk", cssVar: "--muted" },
|
||||
{ name: "Soluk — yazı", cssVar: "--muted-foreground" },
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user