Last active
July 3, 2024 17:22
-
-
Save rwsite/65c631d979687377b14233ece3070cc2 to your computer and use it in GitHub Desktop.
Livewire 3 component - TinyMce editor
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* Livewire 3 component - TinyMce editor | |
* | |
* 1. Add component to LiveWire folder in project | |
* 2. Register component, for example add node to AppServiceProvider boot method: Blade::component('editor', Editor::class); | |
* 3. Register route for file uploader : | |
* Route::middleware(['web', 'auth'])->post('/tinymce/upload', function (Request $request) { | |
* $disk = $request->disk ?? 'public'; | |
* $folder = $request->folder ?? 'editor'; | |
* $file = Storage::disk($disk)->put($folder, $request->file('file'), 'public'); | |
* $url = Storage::disk($disk)->url($file); | |
* return ['location' => $url]; | |
* }); | |
* 4. Include editor in your form: <x-editor wire:model="content"></x-editor> | |
* 5. Include tinyMCE js to <header></header> | |
* 6. Check result https://i.imgur.com/nT4uxIj.png | |
*/ | |
namespace App\Livewire; | |
use Closure; | |
use Illuminate\Contracts\View\View; | |
use Illuminate\View\Component; | |
class Editor extends Component | |
{ | |
public string $uuid; | |
public function __construct( | |
public ?string $label = null, | |
public ?string $hint = null, | |
public ?string $disk = 'public', | |
public ?string $folder = 'editor', | |
public ?array $config = [], | |
// Validations | |
public ?string $errorField = null, | |
public ?string $errorClass = 'text-red-500 label-text-alt p-1', | |
public ?bool $omitError = false, | |
public ?bool $firstErrorOnly = false, | |
) { | |
$this->uuid = "my" . md5(serialize($this)); | |
} | |
public function modelName(): ?string | |
{ | |
return $this->attributes->whereStartsWith('wire:model')->first(); | |
} | |
public function errorFieldName(): ?string | |
{ | |
return $this->errorField ?? $this->modelName(); | |
} | |
public function setup(): string | |
{ | |
$setup = array_merge([ | |
/*'menubar' => false, | |
'automatic_uploads' => true, | |
'remove_script_host' => false,*/ | |
//'height' => 300, | |
'quickbars_insert_toolbar' => false, | |
'branding' => false, | |
'relative_urls' => false, | |
'toolbar' => 'undo redo | blocks fontsize | bold italic underline strikethrough | link image media table mergetags | addcomment showcomments | spellcheckdialog a11ycheck typography | align lineheight | checklist numlist bullist indent outdent | emoticons charmap | removeformat', | |
'quickbars_selection_toolbar' => 'bold italic underline strikethrough | forecolor backcolor | link blockquote removeformat | blocks', | |
], $this->config); | |
$setup['plugins'] = str('advlist autolink lists link image table quickbars insertdatetime media nonbreaking save table directionality')->append($this->config['plugins'] ?? ''); | |
return str(json_encode($setup))->trim('{}')->replace("\"", "'")->toString(); | |
} | |
public function render(): View|Closure|string | |
{ | |
return <<<'HTML' | |
<div> | |
<!-- STANDARD LABEL --> | |
@if($label) | |
<label for="{{ $uuid }}" class="pt-0 label label-text font-semibold"> | |
<span> | |
{{ $label }} | |
@if($attributes->get('required')) | |
<span class="text-error">*</span> | |
@endif | |
</span> | |
</label> | |
@endif | |
<!-- EDITOR --> | |
<div | |
x-data=" | |
{ | |
value: @entangle($attributes->wire('model')), | |
uploadUrl: '/file/upload?disk={{ $disk }}&folder={{ $folder }}&_token={{ csrf_token() }}' | |
}" | |
x-init=" | |
tinymce.init({ | |
{{ $setup() }}, | |
target: $refs.tinymce, | |
readonly: {{ json_encode($attributes->get('readonly') || $attributes->get('disabled')) }}, | |
@if($attributes->get('disabled')) | |
content_style: 'body { opacity: 50% }', | |
@else | |
content_style: 'img { max-width: 100%; height: auto; }', | |
@endif | |
setup: function(editor) { | |
editor.on('keyup', (e) => value = editor.getContent()) | |
editor.on('change', (e) => value = editor.getContent()) | |
editor.on('init', () => editor.setContent(value ?? '')) | |
editor.on('OpenWindow', (e) => tinymce.activeEditor.topLevelWindow = e.dialog) | |
}, | |
file_picker_callback : function(callback, value, meta) { | |
var x = window.innerWidth || document.documentElement.clientWidth || document.getElementsByTagName('body')[0].clientWidth; | |
var y = window.innerHeight|| document.documentElement.clientHeight|| document.getElementsByTagName('body')[0].clientHeight; | |
var cmsURL = '/' + 'laravel-filemanager?editor=' + meta.fieldname; | |
if (meta.filetype == 'image') { | |
cmsURL = cmsURL + '&type=Images'; | |
} else { | |
cmsURL = cmsURL + '&type=Files'; | |
} | |
tinyMCE.activeEditor.windowManager.openUrl({ | |
url : cmsURL, | |
title : 'Filemanager', | |
width : x * 0.8, | |
height : y * 0.8, | |
resizable : 'yes', | |
close_previous : 'no', | |
onMessage: (api, message) => { | |
callback(message.content); | |
} | |
}); | |
} | |
}) | |
" | |
x-on:livewire:navigating.window="tinymce.activeEditor.destroy();" | |
wire:ignore | |
> | |
<input x-ref="tinymce" type="textarea" {{ $attributes->whereDoesntStartWith('wire:model') }} /> | |
</div> | |
<!-- ERROR --> | |
@if(!$omitError && $errors->has($errorFieldName())) | |
@foreach($errors->get($errorFieldName()) as $message) | |
@foreach(Arr::wrap($message) as $line) | |
<div class="{{ $errorClass }}" x-classes="text-red-500 label-text-alt p-1">{{ $line }}</div> | |
@break($firstErrorOnly) | |
@endforeach | |
@break($firstErrorOnly) | |
@endforeach | |
@endif | |
<!-- HINT --> | |
@if($hint) | |
<div class="label-text-alt text-gray-400 pl-1 mt-2">{{ $hint }}</div> | |
@endif | |
</div> | |
HTML; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment