You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
100 lines
2.2 KiB
Svelte
100 lines
2.2 KiB
Svelte
<script lang="ts">
|
|
import { tick, createEventDispatcher } from "svelte";
|
|
|
|
export let description: string;
|
|
export let completed: boolean;
|
|
export let editing: boolean;
|
|
export let busy: boolean;
|
|
|
|
const dispatch = createEventDispatcher<{
|
|
toggleEdit: void;
|
|
saveText: { text: string };
|
|
cancelText: void;
|
|
}>();
|
|
|
|
let el: HTMLElement | null = null;
|
|
|
|
// Mantener el DOM sincronizado cuando se cierra la edición o cambia la descripción
|
|
$: if (el && !editing) {
|
|
el.textContent = description;
|
|
}
|
|
|
|
// Enfocar al entrar en modo edición
|
|
$: if (editing) {
|
|
tick().then(() => {
|
|
if (el) {
|
|
el.focus();
|
|
placeCaretAtEnd(el);
|
|
}
|
|
});
|
|
}
|
|
|
|
function placeCaretAtEnd(node: HTMLElement) {
|
|
const range = document.createRange();
|
|
range.selectNodeContents(node);
|
|
range.collapse(false);
|
|
const sel = window.getSelection();
|
|
sel?.removeAllRanges();
|
|
sel?.addRange(range);
|
|
}
|
|
|
|
function normalizeText(s: string): string {
|
|
return s.replace(/\s+/g, " ").trim();
|
|
}
|
|
|
|
export function getCurrentText(): string {
|
|
return normalizeText(el?.textContent || "");
|
|
}
|
|
</script>
|
|
|
|
<div
|
|
tabindex="0"
|
|
class="desc"
|
|
class:editing={editing}
|
|
class:completed
|
|
contenteditable={editing && !completed}
|
|
role="textbox"
|
|
aria-label="Descripción de la tarea"
|
|
spellcheck="true"
|
|
bind:this={el}
|
|
on:dblclick={() => !busy && !completed && dispatch('toggleEdit')}
|
|
on:keydown={(e) => {
|
|
if (e.key === "Escape") {
|
|
e.preventDefault();
|
|
dispatch('cancelText');
|
|
} else if ((e.ctrlKey || e.metaKey) && e.key === "Enter") {
|
|
e.preventDefault();
|
|
dispatch('saveText', { text: getCurrentText() });
|
|
} else if (e.key === "Enter") {
|
|
e.preventDefault();
|
|
}
|
|
}}
|
|
>
|
|
{description}
|
|
</div>
|
|
|
|
<style>
|
|
.desc {
|
|
padding: 8px 4px;
|
|
grid-column: 1/3;
|
|
grid-row: 2/3;
|
|
}
|
|
|
|
.desc.editing {
|
|
outline: 2px solid var(--color-primary);
|
|
outline-offset: 2px;
|
|
background: var(--color-surface);
|
|
padding: 2px 4px;
|
|
border-radius: 4px;
|
|
white-space: normal;
|
|
text-overflow: clip;
|
|
grid-column: 1/3;
|
|
grid-row: 2/3;
|
|
margin: 16px 0;
|
|
}
|
|
|
|
.desc.completed {
|
|
text-decoration: line-through;
|
|
}
|
|
</style>
|