feat: extrae TaskText y TaskMeta para TaskItem
Co-authored-by: aider (openrouter/openai/gpt-5) <aider@aider.chat>main
parent
415548cdce
commit
b02ca36383
@ -0,0 +1,31 @@
|
||||
<script lang="ts">
|
||||
import TaskDueBadge from "$lib/ui/data/task/TaskDueBadge.svelte";
|
||||
|
||||
export let groupLabel: string;
|
||||
export let gc: { border?: string; bg?: string; text?: string } | null = null;
|
||||
export let due_date: string | null = null;
|
||||
</script>
|
||||
|
||||
<span
|
||||
class="group-badge"
|
||||
title="Grupo"
|
||||
style={gc
|
||||
? `--gc-border: ${gc.border}; --gc-bg: ${gc.bg}; --gc-text: ${gc.text};`
|
||||
: undefined}
|
||||
>
|
||||
{groupLabel}
|
||||
</span>
|
||||
{#if due_date}
|
||||
<TaskDueBadge {due_date} />
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
.group-badge {
|
||||
padding: 2px 6px;
|
||||
border-radius: 6px;
|
||||
border: 1px solid var(--gc-border, var(--color-border));
|
||||
background: var(--gc-bg, transparent);
|
||||
color: var(--gc-text, inherit);
|
||||
font-size: 12px;
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,99 @@
|
||||
<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>
|
||||
Loading…
Reference in New Issue