Files
citrus/resources/views/components/placeholder-picker.blade.php
T

276 lines
16 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
// Desteklenen placeholder tipleri ve açıklamaları
$placeholderTypes = [
'text' => ['label' => __('placeholder-picker.type_text'), 'examples' => ['company_name', 'title', 'description', 'subtitle']],
'email' => ['label' => __('placeholder-picker.type_email'), 'examples' => ['contact', 'support', 'info', 'sales']],
'url' => ['label' => __('placeholder-picker.type_url'), 'examples' => ['website', 'facebook', 'twitter', 'linkedin']],
'tel' => ['label' => __('placeholder-picker.type_tel'), 'examples' => ['phone', 'mobile', 'fax', 'hotline']],
'number' => ['label' => __('placeholder-picker.type_number'), 'examples' => ['price', 'quantity', 'age', 'year']],
'textarea' => ['label' => __('placeholder-picker.type_textarea'), 'examples' => ['content', 'about', 'description', 'notes']],
'richtext' => ['label' => __('placeholder-picker.type_richtext'), 'examples' => ['content', 'article', 'post', 'body']],
'markdown' => ['label' => __('placeholder-picker.type_markdown'), 'examples' => ['content', 'documentation', 'readme']],
'code' => ['label' => __('placeholder-picker.type_code'), 'examples' => ['snippet', 'example', 'script']],
'date' => ['label' => __('placeholder-picker.type_date'), 'examples' => ['publish_date', 'event_date', 'birthday']],
'datetime' => ['label' => __('placeholder-picker.type_datetime'), 'examples' => ['created_at', 'updated_at', 'event_time']],
'time' => ['label' => __('placeholder-picker.type_time'), 'examples' => ['start_time', 'end_time', 'opening_time']],
'image' => ['label' => __('placeholder-picker.type_image'), 'examples' => ['logo', 'banner', 'avatar', 'thumbnail']],
'images' => ['label' => __('placeholder-picker.type_images'), 'examples' => ['gallery', 'photos', 'slider_images']],
'file' => ['label' => __('placeholder-picker.type_file'), 'examples' => ['document', 'pdf', 'download']],
'files' => ['label' => __('placeholder-picker.type_files'), 'examples' => ['documents', 'attachments']],
'select' => ['label' => __('placeholder-picker.type_select'), 'examples' => ['category', 'status', 'type']],
'multiselect' => ['label' => __('placeholder-picker.type_multiselect'), 'examples' => ['tags', 'categories', 'features']],
'checkbox' => ['label' => __('placeholder-picker.type_checkbox'), 'examples' => ['is_active', 'is_featured', 'is_published']],
'radio' => ['label' => __('placeholder-picker.type_radio'), 'examples' => ['gender', 'type', 'status']],
'toggle' => ['label' => __('placeholder-picker.type_toggle'), 'examples' => ['is_active', 'is_enabled', 'is_visible']],
'color' => ['label' => __('placeholder-picker.type_color'), 'examples' => ['primary_color', 'background_color', 'text_color']],
'tags' => ['label' => __('placeholder-picker.type_tags'), 'examples' => ['tags', 'keywords', 'labels']],
];
// Field name'i component'ten al
$fieldName = $fieldName ?? 'html_content';
@endphp
<div
x-data="{
showPicker: false,
selectedType: null,
insertPlaceholder(placeholder) {
const placeholderText = '{' + placeholder + '}';
// Monaco Editor instance'ını bul
const editorContainer = document.querySelector('[data-field-name=\'{{ $fieldName }}\']');
let monacoEditorInstance = null;
let currentValue = '';
let cursorPosition = null;
// Monaco Editor'dan cursor pozisyonunu al
if (editorContainer && window.monaco) {
const editorInstances = window.monaco.editor.getEditors();
for (let editor of editorInstances) {
const container = editor.getContainerDomNode();
if (container && container.closest && container.closest('[data-field-name=\'{{ $fieldName }}\']')) {
monacoEditorInstance = editor;
const model = editor.getModel();
currentValue = model.getValue();
const selection = editor.getSelection();
if (selection) {
cursorPosition = model.getOffsetAt(selection.getStartPosition());
} else {
cursorPosition = currentValue.length;
}
break;
}
}
}
// Monaco Editor varsa, doğrudan onu kullan
if (monacoEditorInstance) {
const model = monacoEditorInstance.getModel();
const newValue = currentValue.slice(0, cursorPosition) + placeholderText + currentValue.slice(cursorPosition);
// Monaco Editor'da değeri güncelle
// pushEditOperations kullanarak undo/redo desteği ile güncelle
monacoEditorInstance.executeEdits('placeholder-insert', [{
range: new window.monaco.Range(
model.getPositionAt(cursorPosition).lineNumber,
model.getPositionAt(cursorPosition).column,
model.getPositionAt(cursorPosition).lineNumber,
model.getPositionAt(cursorPosition).column
),
text: placeholderText
}]);
// Cursor pozisyonunu güncelle
const newPosition = model.getPositionAt(cursorPosition + placeholderText.length);
monacoEditorInstance.setPosition(newPosition);
monacoEditorInstance.focus();
// Filament form state'ini güncellemek için Livewire component'ini bul
// Monaco Editor değişikliği otomatik algılar ama form state'ini manuel güncelle
setTimeout(() => {
// Filament CodeEditor'ın hidden input'unu bul ve güncelle
const hiddenInput = editorContainer.querySelector('input[type="hidden"]');
if (hiddenInput) {
hiddenInput.value = newValue;
hiddenInput.dispatchEvent(new Event('input', { bubbles: true }));
hiddenInput.dispatchEvent(new Event('change', { bubbles: true }));
}
// Livewire component'ini bul ve güncelle
const wireElement = editorContainer.closest('[wire\\:id]');
if (wireElement && window.Livewire) {
const wireId = wireElement.getAttribute('wire:id');
const component = window.Livewire.find(wireId);
if (component) {
// Livewire 3 API: component.set() kullan
const fieldPath = 'data.' + '{{ $fieldName }}';
try {
// Önce mevcut değeri kontrol et
if (component.get(fieldPath) !== undefined) {
component.set(fieldPath, newValue);
} else if (component.get('data.html_content') !== undefined) {
component.set('data.html_content', newValue);
} else if (component.get('html_content') !== undefined) {
component.set('html_content', newValue);
}
} catch (e) {
console.warn('Livewire set failed:', e);
}
}
}
}, 100);
return;
}
// Livewire component'ini bul ve güncelle (Monaco Editor yoksa)
if (window.Livewire) {
const wireElement = document.querySelector('[wire\\:id]');
if (wireElement) {
const wireId = wireElement.getAttribute('wire:id');
const component = window.Livewire.find(wireId);
if (component) {
// Mevcut değeri al
let fieldValue = '';
if (component.get('data.html_content') !== undefined) {
fieldValue = component.get('data.html_content') || '';
} else if (component.get('html_content') !== undefined) {
fieldValue = component.get('html_content') || '';
} else {
const fieldPath = 'data.' + '{{ $fieldName }}';
fieldValue = component.get(fieldPath) || '';
}
// Cursor pozisyonu yoksa sona ekle
if (cursorPosition === null) {
cursorPosition = fieldValue.length;
}
// Placeholder'ı cursor pozisyonuna ekle
const newValue = fieldValue.slice(0, cursorPosition) + placeholderText + fieldValue.slice(cursorPosition);
// Livewire state'ini güncelle (Livewire 3 API)
if (component.get('data.html_content') !== undefined) {
component.set('data.html_content', newValue);
} else if (component.get('html_content') !== undefined) {
component.set('html_content', newValue);
} else {
component.set('data.' + '{{ $fieldName }}', newValue);
}
return;
}
}
}
// Fallback: Textarea veya input elementini bul
const formField = document.querySelector(`input[name*='{{ $fieldName }}'], textarea[name*='{{ $fieldName }}']`);
if (formField) {
const fieldValue = formField.value || '';
const fieldCursorPosition = formField.selectionStart || fieldValue.length;
const newValue = fieldValue.slice(0, fieldCursorPosition) + placeholderText + fieldValue.slice(fieldCursorPosition);
formField.value = newValue;
formField.setSelectionRange(fieldCursorPosition + placeholderText.length, fieldCursorPosition + placeholderText.length);
// Form event'lerini tetikle
formField.dispatchEvent(new Event('input', { bubbles: true }));
formField.dispatchEvent(new Event('change', { bubbles: true }));
}
}
}"
class="fi-input-wrp"
>
<div class="rounded-lg border border-gray-200 bg-white dark:border-gray-700 dark:bg-gray-800">
<div
@click="showPicker = !showPicker"
class="flex items-center justify-between p-3 cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors"
>
<div class="flex items-center gap-2">
<svg class="w-5 h-5 text-gray-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7a2 2 0 010 2.828l-7 7a2 2 0 01-2.828 0l-7-7A1.994 1.994 0 013 12V7a4 4 0 014-4z"></path>
</svg>
<span class="text-sm font-medium text-gray-700 dark:text-gray-300">
{{ __('placeholder-picker.title') }}
</span>
</div>
<svg
x-show="!showPicker"
class="w-4 h-4 text-gray-400 transition-transform"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"></path>
</svg>
<svg
x-show="showPicker"
class="w-4 h-4 text-gray-400 transition-transform"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 15l7-7 7 7"></path>
</svg>
</div>
<div
x-show="showPicker"
x-collapse
class="border-t border-gray-200 dark:border-gray-700 max-h-96 overflow-y-auto"
>
<div class="p-4 space-y-4">
@foreach($placeholderTypes as $type => $info)
<div class="space-y-2">
<h4 class="text-xs font-semibold text-gray-700 dark:text-gray-300 uppercase tracking-wide">
{{ $info['label'] }}
</h4>
<div class="flex flex-wrap gap-2">
@foreach($info['examples'] as $example)
@php
$placeholder = $type . '.' . $example;
@endphp
<button
type="button"
@click="insertPlaceholder('{{ $placeholder }}'); showPicker = false;"
class="inline-flex items-center gap-1 px-3 py-1.5 text-xs font-medium rounded-md bg-gray-100 hover:bg-gray-200 dark:bg-gray-700 dark:hover:bg-gray-600 text-gray-700 dark:text-gray-300 transition-colors border border-gray-300 dark:border-gray-600"
title="{{ __('placeholder-picker.click_to_insert') }}"
>
<code class="text-xs">{{ '{' . $placeholder . '}' }}</code>
</button>
@endforeach
</div>
</div>
@endforeach
<!-- Özel placeholder'lar -->
<div class="pt-2 border-t border-gray-200 dark:border-gray-700">
<h4 class="text-xs font-semibold text-gray-700 dark:text-gray-300 uppercase tracking-wide mb-2">
{{ __('placeholder-picker.special_placeholders') }}
</h4>
<div class="flex flex-wrap gap-2">
<button
type="button"
@click="insertPlaceholder('menu'); showPicker = false;"
class="inline-flex items-center gap-1 px-3 py-1.5 text-xs font-medium rounded-md bg-blue-100 hover:bg-blue-200 dark:bg-blue-900 dark:hover:bg-blue-800 text-blue-700 dark:text-blue-300 transition-colors border border-blue-300 dark:border-blue-700"
>
<code class="text-xs">{menu}</code>
</button>
<button
type="button"
@click="insertPlaceholder('staticMenu'); showPicker = false;"
class="inline-flex items-center gap-1 px-3 py-1.5 text-xs font-medium rounded-md bg-blue-100 hover:bg-blue-200 dark:bg-blue-900 dark:hover:bg-blue-800 text-blue-700 dark:text-blue-300 transition-colors border border-blue-300 dark:border-blue-700"
>
<code class="text-xs">{staticMenu}</code>
</button>
</div>
</div>
</div>
</div>
</div>
</div>