Files
frontend-svelte/src/shared/ui/ContentEditable/ContentEditable.svelte
2026-01-30 17:44:18 +03:00

69 lines
1.7 KiB
Svelte

<!--
Component: ContentEditable
Provides a contenteditable div with custom font and text properties.
-->
<script lang="ts">
interface Props {
/**
* Visible text (bindable)
*/
text: string;
/**
* Font size in pixels
*/
fontSize?: number;
/**
* Line height
*/
lineHeight?: number;
/**
* Letter spacing in pixels
*/
letterSpacing?: number;
}
let {
text = $bindable('The quick brown fox jumps over the lazy dog.'),
fontSize = 48,
lineHeight = 1.2,
letterSpacing = 0,
}: Props = $props();
let element: HTMLDivElement | undefined = $state();
// Initial Sync: Set the text ONLY ONCE when the element is created.
// This prevents Svelte from "owning" the innerHTML/innerText.
$effect(() => {
if (element && element.innerText !== text) {
element.innerText = text;
}
});
// Handle changes: Update the outer state without re-rendering the div.
function handleInput(e: Event) {
const target = e.target as HTMLDivElement;
// Update the bindable prop directly
text = target.innerText;
}
</script>
<div
bind:this={element}
contenteditable="plaintext-only"
spellcheck="false"
role="textbox"
tabindex="0"
data-placeholder="Type something to test..."
oninput={handleInput}
class="
w-full min-h-[1.2em] outline-none transition-all duration-200
empty:before:content-[attr(data-placeholder)] empty:before:text-slate-400
selection:bg-indigo-100 selection:text-indigo-900
caret-indigo-500 focus:outline-none
"
style:font-size="{fontSize}px"
style:line-height={lineHeight}
style:letter-spacing="{letterSpacing}em"
>
</div>