This commit is contained in:
Mark Gregory
2026-06-23 11:46:29 +01:00
parent e8c8dc72ba
commit cd52a0bbda
8 changed files with 127 additions and 166 deletions
@@ -220,42 +220,18 @@
</div>
<div class="lg:col-span-3 xl:col-span-6">
<label class="block text-sm font-semibold text-zinc-200" for="body-editor">
Details <span class="helpdesk-required-star">*</span>
</label>
<div class="mt-2 rounded-xl border border-zinc-700 bg-zinc-950 shadow-sm focus-within:border-blue-500 focus-within:ring-2 focus-within:ring-blue-500/40">
<div class="flex flex-col gap-2 border-b border-zinc-700 bg-zinc-900 px-4 py-3 sm:flex-row sm:items-center sm:justify-between">
<div>
<p class="text-sm font-semibold text-zinc-100">Spellcheck writing area</p>
<p class="mt-1 text-xs text-zinc-400">
Misspelled words are underlined by the browser. Right-click or Ctrl-click an underlined word to choose a suggestion.
</p>
</div>
<span class="inline-flex items-center gap-2 rounded-full border border-emerald-500/30 bg-emerald-500/10 px-3 py-1 text-xs font-semibold text-emerald-200"
title="Use your browser context menu on underlined words to view spelling suggestions.">
<i class="fa-solid fa-spell-check" aria-hidden="true"></i>
Suggestions available
</span>
</div>
<textarea id="body-editor" spellcheck="true" lang="en-GB" data-helpdesk-details-editor
data-dusk="ticket-body-editor" dusk="ticket-body-editor" rows="12"
class="block min-h-[18rem] w-full resize-y rounded-b-xl border-0 bg-zinc-950 px-4 py-3 text-sm leading-6 text-zinc-100 shadow-none outline-none placeholder:text-zinc-500 focus:ring-0"
placeholder="Describe the issue, expected outcome, steps to reproduce and any useful context...">{{ $body ?? '' }}</textarea>
</div>
<textarea id="body" spellcheck="true" lang="en-GB" wire:model="body" class="sr-only" tabindex="-1" aria-hidden="true"></textarea>
<x-helpdesk::html-editor
label="Details"
rows="12"
placeholder="Describe the issue, expected outcome, steps to reproduce and any useful context..."
wire:model="body"
editor-dusk="ticket-body-editor"
/>
<p class="mt-2 flex items-center gap-2 text-xs font-medium text-zinc-400">
<i class="fa-solid fa-circle-info text-blue-400" aria-hidden="true"></i>
Use right-click or Ctrl-click on a red-underlined word to see browser spelling suggestions.
Rich formatting is available. Browser spelling suggestions appear by right-clicking or Ctrl-clicking red-underlined words.
</p>
@error('body')
<p class="mt-2 text-sm text-red-500">{{ $message }}</p>
@enderror
</div>
<div class="lg:col-span-3 xl:col-span-6">
@@ -323,97 +299,17 @@
</form>
<script>
(() => {
const initialisedEditors = window.helpdeskTicketCreateEditors ??= new WeakSet();
const syncEditorToLivewire = (form, editor, bodyField) => {
const value = (editor.value ?? editor.textContent ?? '').trim();
bodyField.value = value;
bodyField.dispatchEvent(new Event('input', { bubbles: true }));
bodyField.dispatchEvent(new Event('change', { bubbles: true }));
const livewireRoot = form.closest('[wire\:id]');
const livewireId = livewireRoot?.getAttribute('wire:id');
if (livewireId && window.Livewire?.find(livewireId)) {
window.Livewire.find(livewireId).set('body', value, false);
}
};
const resetTicketCreateFormDom = () => {
document.querySelectorAll('.helpdesk-ticket-form').forEach((form) => {
const editor = form.querySelector('[data-helpdesk-details-editor]');
const bodyField = form.querySelector('#body');
const subjectField = form.querySelector('#subject');
const attachmentField = form.querySelector('#attachments');
if (editor) {
editor.value = '';
}
if (bodyField) {
bodyField.value = '';
bodyField.dispatchEvent(new Event('input', { bubbles: true }));
bodyField.dispatchEvent(new Event('change', { bubbles: true }));
}
if (subjectField) {
subjectField.value = '';
subjectField.dispatchEvent(new Event('input', { bubbles: true }));
subjectField.dispatchEvent(new Event('change', { bubbles: true }));
}
if (attachmentField) {
attachmentField.value = '';
}
if (window.clearHelpdeskFormControls) {
window.clearHelpdeskFormControls();
}
const livewireRoot = form.closest('[wire\:id]');
const livewireId = livewireRoot?.getAttribute('wire:id');
const component = livewireId && window.Livewire?.find(livewireId);
if (component) {
component.set('subject', '', false);
component.set('body', '', false);
}
const resetTicketCreateFiles = () => {
document.querySelectorAll('.helpdesk-ticket-form #attachments').forEach((input) => {
input.value = '';
});
};
const initialiseTicketCreateEditor = () => {
document.querySelectorAll('.helpdesk-ticket-form').forEach((form) => {
const editor = form.querySelector('[data-helpdesk-details-editor]');
const bodyField = form.querySelector('#body');
if (!editor || !bodyField || initialisedEditors.has(editor)) {
return;
}
initialisedEditors.add(editor);
const syncBody = () => syncEditorToLivewire(form, editor, bodyField);
editor.addEventListener('input', syncBody);
editor.addEventListener('keyup', syncBody);
editor.addEventListener('paste', () => window.setTimeout(syncBody, 0));
editor.addEventListener('blur', syncBody);
form.addEventListener('submit', syncBody, { capture: true });
});
};
document.addEventListener('helpdesk-form-cleared', resetTicketCreateFormDom);
document.addEventListener('helpdesk-editor-reset', resetTicketCreateFormDom);
document.addEventListener('helpdesk-form-cleared', resetTicketCreateFiles);
if (window.Livewire?.on) {
window.Livewire.on('helpdesk-form-cleared', resetTicketCreateFormDom);
window.Livewire.on('helpdesk-editor-reset', resetTicketCreateFormDom);
window.Livewire.on('helpdesk-form-cleared', resetTicketCreateFiles);
}
document.addEventListener('DOMContentLoaded', initialiseTicketCreateEditor);
document.addEventListener('livewire:navigated', initialiseTicketCreateEditor);
initialiseTicketCreateEditor();
})();
</script>
</div>
</div>
@@ -403,27 +403,40 @@
</div>
<div>
<label for="ticket-comment-body"
<label for="ticket-comment-body-editor"
class="mb-2 block text-xs font-bold text-zinc-700 dark:text-zinc-200">Comment</label>
<div class="rounded-xl border border-zinc-300 bg-white shadow-sm focus-within:border-blue-500 focus-within:ring-2 focus-within:ring-blue-500 dark:border-zinc-700 dark:bg-zinc-950">
<div class="flex flex-col gap-1 border-b border-zinc-200 px-4 py-3 dark:border-zinc-700 sm:flex-row sm:items-center sm:justify-between">
<p class="text-xs font-bold uppercase tracking-[0.16em] text-zinc-500 dark:text-zinc-400">Spellcheck reply</p>
<span class="inline-flex items-center gap-2 rounded-full border border-emerald-300 bg-emerald-50 px-3 py-1 text-xs font-bold text-emerald-800 dark:border-emerald-900 dark:bg-emerald-950/60 dark:text-emerald-100">
<i class="fa-solid fa-spell-check" aria-hidden="true"></i>
Right-click suggestions
</span>
<div class="overflow-hidden rounded-xl border border-zinc-300 bg-white shadow-sm focus-within:border-blue-500 focus-within:ring-2 focus-within:ring-blue-500 dark:border-zinc-700 dark:bg-zinc-950">
<div class="flex flex-wrap items-center gap-1 border-b border-zinc-200 bg-zinc-50 p-2 dark:border-zinc-700 dark:bg-zinc-900"
data-helpdesk-html-toolbar="ticket-comment-body-editor">
<button type="button" data-helpdesk-command="bold"
class="rounded-lg px-2 py-1 text-sm font-bold hover:bg-zinc-200 dark:hover:bg-zinc-800">B</button>
<button type="button" data-helpdesk-command="italic"
class="rounded-lg px-2 py-1 text-sm italic hover:bg-zinc-200 dark:hover:bg-zinc-800">I</button>
<button type="button" data-helpdesk-command="underline"
class="rounded-lg px-2 py-1 text-sm underline hover:bg-zinc-200 dark:hover:bg-zinc-800">U</button>
<button type="button" data-helpdesk-command="insertUnorderedList"
class="rounded-lg px-2 py-1 text-sm hover:bg-zinc-200 dark:hover:bg-zinc-800">• List</button>
<button type="button" data-helpdesk-command="insertOrderedList"
class="rounded-lg px-2 py-1 text-sm hover:bg-zinc-200 dark:hover:bg-zinc-800">1. List</button>
<button type="button" data-helpdesk-block="blockquote"
class="rounded-lg px-2 py-1 text-sm hover:bg-zinc-200 dark:hover:bg-zinc-800">Quote</button>
<button type="button" data-helpdesk-block="code"
class="rounded-lg px-2 py-1 text-sm hover:bg-zinc-200 dark:hover:bg-zinc-800">Code</button>
</div>
<textarea dusk="comment-body" spellcheck="true" lang="en-GB" data-dusk="comment-body" id="ticket-comment-body"
name="commentBody" required rows="8"
class="block min-h-[12rem] w-full resize-y rounded-b-xl border-0 bg-white p-4 text-sm leading-6 text-zinc-900 shadow-none focus:outline-none focus:ring-0 dark:bg-zinc-950 dark:text-zinc-100"
placeholder="Write a public reply or internal note. Right-click or Ctrl-click underlined words for spelling suggestions.">{{ strip_tags($oldCommentBody) }}</textarea>
<div id="ticket-comment-body-editor" dusk="comment-body" data-dusk="comment-body"
data-helpdesk-rich-editor data-helpdesk-sync-target="ticket-comment-body"
contenteditable="true" spellcheck="true" lang="en-GB" autocorrect="on" autocomplete="on"
class="prose prose-sm min-h-[12rem] max-w-none bg-white p-4 text-sm leading-6 text-zinc-900 outline-none dark:prose-invert dark:bg-zinc-950 dark:text-zinc-100"
data-placeholder="Write a public reply or internal note. Right-click or Ctrl-click underlined words for spelling suggestions.">{!! $oldCommentBody !!}</div>
<textarea id="ticket-comment-body" name="commentBody" required class="sr-only" tabindex="-1" aria-hidden="true">{{ $oldCommentBody }}</textarea>
</div>
<p class="mt-2 flex items-center gap-2 text-xs font-medium text-zinc-500 dark:text-zinc-400">
<i class="fa-solid fa-circle-info text-blue-500" aria-hidden="true"></i>
Browser suggestions appear from the context menu on underlined words.
Rich formatting is available. Browser suggestions appear from the context menu on underlined words.
</p>
</div>