143 lines
7.2 KiB
PHP
Executable File
143 lines
7.2 KiB
PHP
Executable File
@props([
|
|
'model' => 'body',
|
|
'label' => 'Details',
|
|
'placeholder' => 'Describe the ticket, include steps to reproduce, expected result, actual result and any useful technical details.',
|
|
'rows' => 10,
|
|
'editorDusk' => null,
|
|
])
|
|
|
|
<div
|
|
class="space-y-2"
|
|
x-data="{
|
|
model: @js($model),
|
|
value: @entangle($model),
|
|
init() {
|
|
this.$nextTick(() => {
|
|
this.writeEditorValue(this.value || '');
|
|
});
|
|
},
|
|
writeEditorValue(value) {
|
|
if (!this.$refs.editor) {
|
|
return;
|
|
}
|
|
|
|
const nextValue = value || '';
|
|
const currentValue = this.$refs.editor.innerText || '';
|
|
|
|
if (document.activeElement === this.$refs.editor && currentValue !== '' && currentValue !== nextValue) {
|
|
return;
|
|
}
|
|
|
|
this.$refs.editor.textContent = nextValue;
|
|
},
|
|
syncToLivewire(live = true) {
|
|
this.value = this.$refs.editor?.innerText || '';
|
|
|
|
if (this.$refs.input) {
|
|
this.$refs.input.value = this.value;
|
|
}
|
|
|
|
this.$wire.set(this.model, this.value, live);
|
|
},
|
|
resetEditor() {
|
|
this.value = '';
|
|
this.writeEditorValue('');
|
|
this.syncToLivewire(true);
|
|
},
|
|
runCommand(command) {
|
|
this.$refs.editor?.focus();
|
|
document.execCommand(command, false, null);
|
|
this.syncToLivewire(true);
|
|
},
|
|
wrapBlock(tagName) {
|
|
this.$refs.editor?.focus();
|
|
document.execCommand('formatBlock', false, tagName);
|
|
this.syncToLivewire(true);
|
|
},
|
|
}"
|
|
x-on:helpdesk-editor-sync.window="if (! $event.detail?.model || $event.detail.model === @js($model)) syncToLivewire($event.detail?.live ?? true)"
|
|
x-on:helpdesk-editor-reset.window="if (! $event.detail?.model || $event.detail.model === @js($model)) resetEditor()"
|
|
>
|
|
<label for="{{ $model }}-editor" class="block text-sm font-semibold text-zinc-200">
|
|
{{ $label }}
|
|
</label>
|
|
|
|
<div class="overflow-hidden rounded-2xl border border-zinc-700 bg-zinc-950 shadow-sm">
|
|
<div class="flex flex-wrap items-center gap-1.5 border-b border-zinc-700 bg-zinc-900/80 px-4 py-2 text-sm text-zinc-300" data-helpdesk-editor-toolbar>
|
|
<span class="mr-2 rounded-lg bg-zinc-800 px-2 py-1 text-xs font-black uppercase tracking-wide text-zinc-100">Spellcheck enabled</span>
|
|
|
|
<button type="button" x-on:click="runCommand('bold')" data-helpdesk-editor-command="bold"
|
|
class="inline-flex min-h-8 items-center justify-center rounded-lg border border-zinc-700 bg-zinc-950 px-3 py-1.5 text-xs font-black text-zinc-100 shadow-sm transition hover:bg-zinc-800 focus:outline-none focus:ring-2 focus:ring-blue-400/70"
|
|
aria-label="Bold selected text">
|
|
B
|
|
</button>
|
|
<button type="button" x-on:click="runCommand('italic')" data-helpdesk-editor-command="italic"
|
|
class="inline-flex min-h-8 items-center justify-center rounded-lg border border-zinc-700 bg-zinc-950 px-3 py-1.5 text-xs font-black italic text-zinc-100 shadow-sm transition hover:bg-zinc-800 focus:outline-none focus:ring-2 focus:ring-blue-400/70"
|
|
aria-label="Italic selected text">
|
|
I
|
|
</button>
|
|
<button type="button" x-on:click="runCommand('underline')" data-helpdesk-editor-command="underline"
|
|
class="inline-flex min-h-8 items-center justify-center rounded-lg border border-zinc-700 bg-zinc-950 px-3 py-1.5 text-xs font-black underline text-zinc-100 shadow-sm transition hover:bg-zinc-800 focus:outline-none focus:ring-2 focus:ring-blue-400/70"
|
|
aria-label="Underline selected text">
|
|
U
|
|
</button>
|
|
<button type="button" x-on:click="runCommand('insertUnorderedList')" data-helpdesk-editor-command="insertUnorderedList"
|
|
class="inline-flex min-h-8 items-center justify-center rounded-lg border border-zinc-700 bg-zinc-950 px-3 py-1.5 text-xs font-black text-zinc-100 shadow-sm transition hover:bg-zinc-800 focus:outline-none focus:ring-2 focus:ring-blue-400/70">
|
|
• List
|
|
</button>
|
|
<button type="button" x-on:click="runCommand('insertOrderedList')" data-helpdesk-editor-command="insertOrderedList"
|
|
class="inline-flex min-h-8 items-center justify-center rounded-lg border border-zinc-700 bg-zinc-950 px-3 py-1.5 text-xs font-black text-zinc-100 shadow-sm transition hover:bg-zinc-800 focus:outline-none focus:ring-2 focus:ring-blue-400/70">
|
|
1. List
|
|
</button>
|
|
<button type="button" x-on:click="wrapBlock('blockquote')" data-helpdesk-editor-command="blockquote"
|
|
class="inline-flex min-h-8 items-center justify-center rounded-lg border border-zinc-700 bg-zinc-950 px-3 py-1.5 text-xs font-black text-zinc-100 shadow-sm transition hover:bg-zinc-800 focus:outline-none focus:ring-2 focus:ring-blue-400/70">
|
|
Quote
|
|
</button>
|
|
<button type="button" x-on:click="wrapBlock('pre')" data-helpdesk-editor-command="pre"
|
|
class="inline-flex min-h-8 items-center justify-center rounded-lg border border-zinc-700 bg-zinc-950 px-3 py-1.5 text-xs font-black text-zinc-100 shadow-sm transition hover:bg-zinc-800 focus:outline-none focus:ring-2 focus:ring-blue-400/70">
|
|
Code
|
|
</button>
|
|
</div>
|
|
|
|
<div
|
|
id="{{ $model }}-editor"
|
|
x-ref="editor"
|
|
contenteditable="true"
|
|
spellcheck="true"
|
|
lang="en-GB"
|
|
autocorrect="on"
|
|
autocomplete="on"
|
|
autocapitalize="sentences"
|
|
inputmode="text"
|
|
aria-multiline="true"
|
|
data-placeholder="{{ $placeholder }}"
|
|
data-wire-model="{{ $model }}"
|
|
data-helpdesk-sync-target="{{ $model }}"
|
|
data-helpdesk-editor-model="{{ $model }}"
|
|
@if ($editorDusk) data-dusk="{{ $editorDusk }}" dusk="{{ $editorDusk }}" @endif
|
|
x-on:paste="syncToLivewire(false)"
|
|
x-on:blur="syncToLivewire(true)"
|
|
class="helpdesk-rich-editor block min-h-[14rem] w-full overflow-y-auto whitespace-pre-wrap break-words border-0 bg-zinc-950 px-5 py-4 text-base leading-7 text-zinc-100 outline-none transition placeholder:text-zinc-500 empty:before:pointer-events-none empty:before:text-zinc-500 empty:before:content-[attr(data-placeholder)] focus:ring-2 focus:ring-blue-500"
|
|
></div>
|
|
|
|
<textarea aria-label="Form Field"
|
|
x-ref="input"
|
|
wire:model.defer="{{ $model }}"
|
|
spellcheck="true"
|
|
lang="en-GB"
|
|
autocorrect="on"
|
|
autocomplete="on"
|
|
autocapitalize="sentences"
|
|
inputmode="text"
|
|
class="sr-only"
|
|
tabindex="-1"
|
|
aria-hidden="true"
|
|
data-helpdesk-spellcheck-mirror="{{ $model }}"
|
|
></textarea>
|
|
</div>
|
|
|
|
<p class="text-xs font-semibold text-zinc-400">
|
|
Browser spellcheck stays active in the native editable area. Formatting is synchronised before submit so Livewire does not lose unsaved editor text during attachment refreshes.
|
|
</p>
|
|
</div>
|