mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-19 02:01:22 +02:00
fix(workbench): set SceneHeader text via refs instead of inline mustache
Prettier kept reformatting the <h1>/<p> bodies onto their own indented lines, which contenteditable renders verbatim — the user saw leading/trailing whitespace inside the edit buffer. prettier-ignore only applies to the immediately next node, and the svelte-ignore comment was taking that slot, so the directive never reached the element. Rewire with bind:this + two $effects that set textContent whenever the scene prop changes and the element isn't currently focused. Side benefit: a scene name synced from another device now updates the header live without interrupting an in-progress edit. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
e2ea0cd3b8
commit
6e842a83c9
1 changed files with 27 additions and 12 deletions
|
|
@ -17,10 +17,29 @@
|
|||
|
||||
const { scene }: Props = $props();
|
||||
|
||||
// Helpers for the contenteditable flow. We don't bind:textContent
|
||||
// because Svelte's bindings re-render while the user is typing and
|
||||
// would fight the caret. Instead we read textContent on blur / key
|
||||
// events and commit once.
|
||||
// We avoid inline mustache interpolation inside the contenteditable
|
||||
// elements because Prettier reformats the template and leaves
|
||||
// literal leading/trailing whitespace inside the element — which
|
||||
// contenteditable preserves as part of its edit buffer. Instead,
|
||||
// we bind element refs and set textContent via $effect whenever the
|
||||
// scene value changes and the user isn't actively editing. This
|
||||
// also lets external updates (e.g. a rename synced from another
|
||||
// device) refresh the visible text without fighting the caret.
|
||||
let nameEl = $state<HTMLHeadingElement | null>(null);
|
||||
let descEl = $state<HTMLParagraphElement | null>(null);
|
||||
|
||||
$effect(() => {
|
||||
if (!nameEl || !scene) return;
|
||||
if (document.activeElement === nameEl) return;
|
||||
if (nameEl.textContent !== scene.name) nameEl.textContent = scene.name;
|
||||
});
|
||||
|
||||
$effect(() => {
|
||||
if (!descEl || !scene) return;
|
||||
if (document.activeElement === descEl) return;
|
||||
const next = scene.description ?? '';
|
||||
if (descEl.textContent !== next) descEl.textContent = next;
|
||||
});
|
||||
|
||||
function commitName(el: HTMLElement, current: string) {
|
||||
const next = (el.textContent ?? '').trim();
|
||||
|
|
@ -75,9 +94,9 @@
|
|||
|
||||
{#if scene}
|
||||
<div class="scene-header">
|
||||
<!-- prettier-ignore -->
|
||||
<!-- svelte-ignore a11y_no_noninteractive_element_to_interactive_role -->
|
||||
<h1
|
||||
bind:this={nameEl}
|
||||
class="scene-name"
|
||||
contenteditable="plaintext-only"
|
||||
spellcheck="false"
|
||||
|
|
@ -87,12 +106,10 @@
|
|||
onkeydown={handleNameKey}
|
||||
onfocus={handleFocus}
|
||||
onblur={(e) => commitName(e.currentTarget, scene.name)}
|
||||
>
|
||||
{scene.name}
|
||||
</h1>
|
||||
<!-- prettier-ignore -->
|
||||
></h1>
|
||||
<!-- svelte-ignore a11y_no_noninteractive_element_to_interactive_role -->
|
||||
<p
|
||||
bind:this={descEl}
|
||||
class="scene-desc"
|
||||
class:placeholder={!scene.description}
|
||||
contenteditable="plaintext-only"
|
||||
|
|
@ -104,9 +121,7 @@
|
|||
onkeydown={handleDescKey}
|
||||
onfocus={handleFocus}
|
||||
onblur={(e) => commitDescription(e.currentTarget, scene.description ?? '')}
|
||||
>
|
||||
{scene.description ?? ''}
|
||||
</p>
|
||||
></p>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue