Files
citrus/resources/views/filament/components/markdown-preview.blade.php
T

345 lines
14 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
@php
// Server-side: get initial values for the header metadata
$accent = $get('meta.accent_color') ?? 'indigo';
$accentColor = match($accent) {
'emerald' => '#059669',
'cyberpunk' => '#ec4899',
'coral' => '#ea580c',
'amber' => '#d97706',
default => '#4f46e5',
};
$accentGradient = match($accent) {
'emerald' => 'linear-gradient(135deg, #059669 0%, #10b981 100%)',
'cyberpunk' => 'linear-gradient(135deg, #ec4899 0%, #f43f5e 100%)',
'coral' => 'linear-gradient(135deg, #ea580c 0%, #f97316 100%)',
'amber' => 'linear-gradient(135deg, #d97706 0%, #f59e0b 100%)',
default => 'linear-gradient(135deg, #4f46e5 0%, #6366f1 100%)',
};
@endphp
{{-- Google Fonts --}}
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=Outfit:wght@400;500;600;700;800&display=swap" rel="stylesheet">
{{-- Marked.js for client-side markdown rendering --}}
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
<div
x-data="{
renderedHtml: '',
init() {
this.renderMarkdown();
// Watch for CodeEditor changes via MutationObserver + polling
this.startWatching();
},
getContentValue() {
// Try multiple methods to read the CodeEditor content
// Method 1: Livewire $wire
if (typeof $wire !== 'undefined' && $wire.data && $wire.data.content) {
return $wire.data.content;
}
// Method 2: Find Monaco/CodeMirror editor instances
const editorWrappers = document.querySelectorAll('[data-field-name=\"content\"]');
for (const wrapper of editorWrappers) {
// CodeMirror 6
const cmEl = wrapper.querySelector('.cm-content');
if (cmEl) {
return cmEl.textContent || '';
}
}
// Method 3: hidden input/textarea
const hiddenInputs = document.querySelectorAll('textarea[wire\\:model\\.live=\"data.content\"], textarea[wire\\:model=\"data.content\"], input[name=\"data.content\"]');
for (const inp of hiddenInputs) {
if (inp.value) return inp.value;
}
// Method 4: any textarea with content field name
const anyTextarea = document.querySelector('[x-ref=\"content\"], textarea[id*=\"content\"]');
if (anyTextarea && anyTextarea.value) return anyTextarea.value;
return '';
},
renderMarkdown() {
const raw = this.getContentValue();
if (raw && raw.trim()) {
try {
this.renderedHtml = marked.parse(raw, { gfm: true, breaks: true });
} catch(e) {
this.renderedHtml = '<p style=\"color:red;\">Markdown parse error</p>';
}
} else {
this.renderedHtml = '';
}
},
startWatching() {
// Poll every 500ms to pick up CodeEditor changes
setInterval(() => { this.renderMarkdown(); }, 500);
// Also observe Livewire morphs
if (typeof Livewire !== 'undefined') {
Livewire.hook('morph.updated', () => {
this.$nextTick(() => this.renderMarkdown());
});
}
}
}"
class="md-preview-root"
style="min-height: 500px; font-family: 'Inter', system-ui, -apple-system, sans-serif;"
>
{{-- Browser Chrome Mockup --}}
<div style="border: 1px solid #e2e8f0; border-radius: 12px; overflow: hidden; box-shadow: 0 4px 24px rgba(0,0,0,0.08); background: #f8fafc; height: 700px; display: flex; flex-direction: column;">
{{-- Title Bar --}}
<div style="background: #f1f5f9; border-bottom: 1px solid #e2e8f0; padding: 10px 16px; display: flex; align-items: center; gap: 12px; flex-shrink: 0;">
{{-- Traffic Lights --}}
<div style="display: flex; gap: 6px; flex-shrink: 0;">
<span style="width: 12px; height: 12px; border-radius: 50%; background: #ff5f57;"></span>
<span style="width: 12px; height: 12px; border-radius: 50%; background: #febc2e;"></span>
<span style="width: 12px; height: 12px; border-radius: 50%; background: #28c840;"></span>
</div>
{{-- URL Bar --}}
<div style="flex: 1; max-width: 480px; margin: 0 auto; background: white; border: 1px solid #e2e8f0; border-radius: 6px; padding: 5px 10px; display: flex; align-items: center; gap: 6px; font-size: 12px; color: #94a3b8;">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="#22c55e" style="width: 14px; height: 14px; flex-shrink: 0;">
<path fill-rule="evenodd" d="M10 1a4.5 4.5 0 00-4.5 4.5V9H5a2 2 0 00-2 2v6a2 2 0 002 2h10a2 2 0 002-2v-6a2 2 0 00-2-2h-.5V5.5A4.5 4.5 0 0010 1zm3 8V5.5a3 3 0 10-6 0V9h6z" clip-rule="evenodd" />
</svg>
<span style="font-family: monospace; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;">
truncgil.com/teklif/<strong style="color: #334155;">{{ $get('slug') ?: '...' }}</strong>
</span>
</div>
<div style="width: 60px; flex-shrink: 0;"></div>
</div>
{{-- Page Body with scroll --}}
<div style="flex: 1; overflow-y: auto; background: #f1f5f9; padding: 24px;">
{{-- Document Sheet --}}
<div style="max-width: 800px; margin: 0 auto; background: white; border-radius: 12px; box-shadow: 0 1px 3px rgba(0,0,0,0.06); border: 1px solid #e5e7eb; overflow: hidden;">
{{-- Accent top stripe --}}
<div style="height: 4px; background: {{ $accentGradient }};"></div>
{{-- Document Content --}}
<div style="padding: 32px 40px;">
{{-- Document Header --}}
<div style="margin-bottom: 24px; padding-bottom: 20px; border-bottom: 1px solid #f1f5f9;">
<div style="display: flex; align-items: center; gap: 8px; margin-bottom: 10px;">
<span style="font-size: 9px; text-transform: uppercase; letter-spacing: 1.5px; font-weight: 800; padding: 3px 8px; border-radius: 4px; color: white; background: {{ $accentColor }};">TEKNİK VE MALİ TEKLİF</span>
@if($get('client_name'))
<span style="font-size: 11px; background: #f8fafc; color: #64748b; font-weight: 600; padding: 3px 8px; border-radius: 4px; border: 1px solid #e2e8f0;">{{ $get('client_name') }}</span>
@endif
</div>
<h1 style="font-family: 'Outfit', sans-serif; font-size: 22px; font-weight: 800; color: #0f172a; margin: 0 0 8px 0; line-height: 1.3;">{{ $get('title') ?: 'Teklif Başlığı' }}</h1>
<div style="font-size: 11px; color: #94a3b8; font-weight: 500; display: flex; gap: 12px; flex-wrap: wrap;">
<span>REF: {{ $get('slug') ?: '—' }}</span>
<span></span>
<span style="font-weight: 700; color: #334155;">{{ $get('total_price') ? number_format((float) $get('total_price'), 2, ',', '.') : '—' }} {{ $get('currency') ?: 'TRY' }}</span>
<span></span>
<span>Son Geçerlilik: {{ $get('valid_until') ? date('d.m.Y', strtotime($get('valid_until'))) : '—' }}</span>
</div>
</div>
{{-- Live Rendered Markdown --}}
<div
class="md-preview-body"
x-show="renderedHtml"
x-html="renderedHtml"
></div>
<div
x-show="!renderedHtml"
style="text-align:center;padding:60px 0;color:#94a3b8;font-style:italic;font-size:14px;"
>
✏️ Sol taraftaki editöre Markdown yazın, canlı önizleme burada görünecektir.
</div>
</div>
</div>
</div>
</div>
</div>
<style>
/* ===== Premium Markdown Preview Typography ===== */
.md-preview-body {
font-family: 'Inter', system-ui, -apple-system, sans-serif;
font-size: 14px;
line-height: 1.7;
color: #334155;
}
.md-preview-body h1 {
font-family: 'Outfit', 'Inter', sans-serif;
font-size: 1.5rem;
font-weight: 800;
color: #0f172a;
margin: 2rem 0 0.8rem 0;
line-height: 1.25;
letter-spacing: -0.02em;
}
.md-preview-body h2 {
font-family: 'Outfit', 'Inter', sans-serif;
font-size: 1.25rem;
font-weight: 700;
color: #1e293b;
margin: 2rem 0 0.7rem 0;
padding-bottom: 0.5rem;
border-bottom: 1px solid #f1f5f9;
line-height: 1.3;
}
.md-preview-body h3 {
font-family: 'Outfit', 'Inter', sans-serif;
font-size: 1.1rem;
font-weight: 600;
color: #334155;
margin: 1.5rem 0 0.5rem 0;
}
.md-preview-body h4 {
font-family: 'Outfit', 'Inter', sans-serif;
font-size: 1rem;
font-weight: 600;
color: #475569;
margin: 1.2rem 0 0.5rem 0;
}
.md-preview-body p {
margin: 0 0 1rem 0;
color: #475569;
line-height: 1.7;
}
.md-preview-body a {
color: {{ $accentColor }};
text-decoration: underline;
text-decoration-color: {{ $accentColor }}40;
text-underline-offset: 2px;
}
.md-preview-body a:hover {
text-decoration-color: {{ $accentColor }};
}
.md-preview-body strong {
font-weight: 700;
color: #1e293b;
}
.md-preview-body img {
max-width: 100%;
height: auto;
border-radius: 8px;
margin: 1rem 0;
display: block;
}
.md-preview-body hr {
border: 0;
height: 1px;
background: #e2e8f0;
margin: 1.8rem 0;
}
/* Lists */
.md-preview-body ul, .md-preview-body ol {
padding-left: 1.5rem;
margin: 0 0 1rem 0;
}
.md-preview-body ul { list-style-type: disc; }
.md-preview-body ol { list-style-type: decimal; }
.md-preview-body li {
margin-bottom: 0.3rem;
line-height: 1.6;
color: #475569;
}
/* Tables */
.md-preview-body table {
width: 100%;
border-collapse: collapse;
margin: 1.5rem 0;
font-size: 13px;
border: 1px solid #e2e8f0;
border-radius: 8px;
overflow: hidden;
}
.md-preview-body th {
background: #f8fafc;
color: #0f172a;
font-weight: 600;
text-align: left;
padding: 10px 14px;
border-bottom: 2px solid #e2e8f0;
}
.md-preview-body td {
padding: 10px 14px;
border-bottom: 1px solid #f1f5f9;
color: #475569;
}
.md-preview-body tr:nth-child(even) td {
background: #fafbfc;
}
.md-preview-body tr:last-child td {
border-bottom: none;
}
/* Code */
.md-preview-body code {
font-family: 'SF Mono', 'Fira Code', 'Consolas', monospace;
font-size: 0.85em;
padding: 2px 6px;
border-radius: 4px;
background: #f1f5f9;
color: #e11d48;
}
.md-preview-body pre {
background: #0f172a;
border-radius: 8px;
padding: 16px;
overflow-x: auto;
margin: 1rem 0;
}
.md-preview-body pre code {
background: transparent;
color: #e2e8f0;
padding: 0;
font-size: 13px;
}
/* Blockquotes */
.md-preview-body blockquote {
border-left: 3px solid {{ $accentColor }};
margin: 1rem 0;
padding: 0.5rem 0 0.5rem 1rem;
color: #64748b;
background: #f8fafc;
border-radius: 0 6px 6px 0;
}
.md-preview-body blockquote p {
margin: 0;
}
/* Centered divs */
.md-preview-body [align="center"],
.md-preview-body div[align="center"] {
text-align: center;
}
/* Dark mode overrides */
.dark .md-preview-root > div { background: #1e293b !important; border-color: #334155 !important; }
.dark .md-preview-root > div > div:first-child { background: #0f172a !important; border-color: #334155 !important; }
.dark .md-preview-root > div > div:first-child > div:nth-child(2) { background: #0f172a !important; border-color: #334155 !important; }
.dark .md-preview-root > div > div:last-child { background: #0f172a !important; }
.dark .md-preview-root > div > div:last-child > div { background: #1e293b !important; border-color: #334155 !important; }
.dark .md-preview-body { color: #cbd5e1; }
.dark .md-preview-body h1, .dark .md-preview-body h2 { color: #f1f5f9; }
.dark .md-preview-body h2 { border-color: #334155; }
.dark .md-preview-body h3, .dark .md-preview-body h4 { color: #e2e8f0; }
.dark .md-preview-body p, .dark .md-preview-body li { color: #94a3b8; }
.dark .md-preview-body strong { color: #f1f5f9; }
.dark .md-preview-body th { background: #0f172a; color: #f1f5f9; border-color: #334155; }
.dark .md-preview-body td { border-color: #1e293b; color: #94a3b8; }
.dark .md-preview-body tr:nth-child(even) td { background: #0f172a40; }
.dark .md-preview-body code { background: #334155; color: #fb7185; }
.dark .md-preview-body hr { background: #334155; }
.dark .md-preview-body blockquote { background: #0f172a; color: #94a3b8; }
</style>