Skip to content

Commit 411e727

Browse files
refactor: Simplify dependency component
1 parent c74e2cd commit 411e727

File tree

2 files changed

+91
-120
lines changed

2 files changed

+91
-120
lines changed

src/ui/Dependency.svelte

Lines changed: 86 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<script lang="ts">
2-
import type {Task} from "../Task/Task";
3-
import {computePosition, flip, offset, shift, size} from "@floating-ui/dom";
4-
import type {EditableTask} from "./EditableTask";
2+
import type { Task } from "../Task/Task";
3+
import { computePosition, flip, offset, shift, size } from "@floating-ui/dom";
4+
import type { EditableTask } from "./EditableTask";
55
66
export let task: Task;
77
export let editableTask: EditableTask;
@@ -10,63 +10,26 @@
1010
export let type: "blocking" | "blockedBy";
1111
export let accesskey: (key: string) => string | null;
1212
13-
let existingTasks = type === "blocking" ? editableTask.blocking : editableTask.blockedBy;
14-
15-
console.log(`blockedBy: ${editableTask.blockedBy.map(task => task.description)}`)
13+
const MAX_SEARCH_RESULTS = 20;
1614
1715
let search: string = '';
1816
let searchResults: Task[] | null = null;
1917
let searchIndex: number | null = 0;
20-
21-
let depInputWidth: number;
22-
18+
let inputWidth: number;
2319
let inputFocused = false;
20+
let showDropdown = false;
2421
25-
let displayResultsIfSearchEmpty = false;
26-
27-
function onFocused() {
28-
inputFocused = true;
29-
displayResultsIfSearchEmpty = true;
30-
}
31-
32-
33-
let dependencyInput: HTMLElement;
34-
let dependencyDropdown: HTMLElement;
22+
let input: HTMLElement;
23+
let dropdown: HTMLElement;
3524
36-
function positionDropdown(ref: HTMLElement, content: HTMLElement) {
37-
if (!ref || !content) return;
38-
39-
computePosition(ref, content, {
40-
middleware: [
41-
offset(6),
42-
shift(),
43-
flip(),
44-
size({
45-
apply() {
46-
content && Object.assign(content.style, { width: `${depInputWidth}px` });
47-
},
48-
}),
49-
],
50-
}).then(({ x, y }) => {
51-
Object.assign(content.style, {
52-
left: `${x}px`,
53-
top: `${y}px`,
54-
});
55-
});
56-
}
57-
58-
const _displayableFilePath = (path: string) => {
59-
if (path === task.taskLocation.path) return "";
60-
61-
return path.slice(0,-3);
62-
}
63-
64-
$: {
65-
positionDropdown(dependencyInput, dependencyDropdown);
25+
function addTask(task: Task) {
26+
editableTask[type] = [...editableTask[type], task];
27+
search = '';
28+
inputFocused = false;
6629
}
6730
68-
$: {
69-
searchResults = inputFocused ? generateSearchResults(search) : null;
31+
function removeTask(task: Task) {
32+
editableTask[type] = editableTask[type].filter(item => item !== task);
7033
}
7134
7235
function taskKeydown(e: KeyboardEvent) {
@@ -75,18 +38,18 @@
7538
switch(e.key) {
7639
case "ArrowUp":
7740
e.preventDefault();
78-
if (searchIndex === 0 || searchIndex === null) {
79-
searchIndex = searchResults.length - 1;
80-
} else {
41+
if (!!searchIndex && searchIndex > 0) {
8142
searchIndex -= 1;
43+
} else {
44+
searchIndex = searchResults.length - 1;
8245
}
8346
break;
8447
case "ArrowDown":
8548
e.preventDefault();
86-
if (searchIndex === searchResults.length - 1 || searchIndex === null) {
87-
searchIndex = 0;
88-
} else {
49+
if (!!searchIndex && searchIndex < searchResults.length - 1) {
8950
searchIndex += 1;
51+
} else {
52+
searchIndex = 0;
9053
}
9154
break;
9255
case "Enter":
@@ -103,40 +66,13 @@
10366
searchIndex = 0;
10467
break;
10568
}
106-
searchIndex = searchIndex;
107-
if (searchIndex !== null) {
108-
dependencyDropdown?.getElementsByTagName('li')[searchIndex]?.scrollIntoView(false)
109-
}
110-
}
111-
112-
function showDescriptionTooltip(element: HTMLElement, text: string) {
113-
const tooltip = element.createDiv();
114-
tooltip.addClasses(['tooltip', 'pop-up']);
115-
tooltip.innerText = text;
116-
117-
computePosition(element, tooltip, {
118-
placement: "top",
119-
middleware: [
120-
offset(-18),
121-
shift()
122-
]
123-
}).then(({x, y}) => {
124-
Object.assign(tooltip.style, {
125-
left: `${x}px`,
126-
top: `${y}px`,
127-
});
128-
});
129-
130-
element.addEventListener('mouseleave', () => {
131-
tooltip.remove();
132-
});
69+
searchIndex && dropdown?.getElementsByTagName('li')[searchIndex]?.scrollIntoView({ block: 'nearest' });
13370
}
13471
135-
13672
function generateSearchResults(search: string) {
137-
if (!search && !displayResultsIfSearchEmpty) return [];
73+
if (!search && !showDropdown) return [];
13874
139-
displayResultsIfSearchEmpty = false;
75+
showDropdown = false;
14076
14177
let results = allTasks.filter(task => task.description.toLowerCase().includes(search.toLowerCase()));
14278
@@ -169,35 +105,70 @@
169105
}
170106
});
171107
172-
return results.slice(0,20);
108+
return results.slice(0,MAX_SEARCH_RESULTS);
173109
}
174110
175-
function addTask(task: Task) {
176-
existingTasks = [...existingTasks, task];
177-
if (type === "blocking") {
178-
editableTask.blocking = existingTasks;
179-
} else {
180-
editableTask.blockedBy = existingTasks;
181-
}
182-
search = '';
183-
inputFocused = false;
111+
function onFocused() {
112+
inputFocused = true;
113+
showDropdown = true;
184114
}
185115
186-
function removeTask(task: Task) {
187-
existingTasks = existingTasks.filter(item => item !== task);
188-
if (type === "blocking") {
189-
editableTask.blocking = existingTasks;
190-
} else {
191-
editableTask.blockedBy = existingTasks;
192-
}
116+
function positionDropdown(input: HTMLElement, dropdown: HTMLElement) {
117+
if (!input || !dropdown) return;
118+
119+
computePosition(input, dropdown, {
120+
middleware: [
121+
offset(6),
122+
shift(),
123+
flip(),
124+
size({
125+
apply() {
126+
dropdown && Object.assign(dropdown.style, { width: `${inputWidth}px` });
127+
},
128+
}),
129+
],
130+
}).then(({ x, y }) => {
131+
dropdown.style.left = `${x}px`;
132+
dropdown.style.top = `${y}px`;
133+
});
134+
}
135+
136+
function displayPath(path: string) {
137+
return path === task.taskLocation.path ? "" : path;
138+
}
139+
140+
function showDescriptionTooltip(element: HTMLElement, text: string) {
141+
const tooltip = element.createDiv();
142+
tooltip.addClasses(['tooltip', 'pop-up']);
143+
tooltip.innerText = text;
144+
145+
computePosition(element, tooltip, {
146+
placement: "top",
147+
middleware: [
148+
offset(-18),
149+
shift()
150+
]
151+
}).then(({x, y}) => {
152+
tooltip.style.left = `${x}px`;
153+
tooltip.style.top = `${y}px`;
154+
});
155+
156+
element.addEventListener('mouseleave', () => tooltip.remove());
157+
}
158+
159+
$: {
160+
positionDropdown(input, dropdown);
161+
}
162+
163+
$: {
164+
searchResults = inputFocused ? generateSearchResults(search) : null;
193165
}
194166
</script>
195167

196168
<!-- svelte-ignore a11y-accesskey -->
197-
198-
<span class="input" bind:clientWidth={depInputWidth}>
169+
<span class="input" bind:clientWidth={inputWidth}>
199170
<input
200-
bind:this={dependencyInput}
171+
bind:this={input}
201172
bind:value={search}
202173
on:keydown={(e) => taskKeydown(e)}
203174
on:focus={onFocused}
@@ -211,10 +182,10 @@
211182
</span>
212183
{#if searchResults && searchResults.length !== 0}
213184
<ul class="suggested-tasks"
214-
bind:this={dependencyDropdown}
185+
bind:this={dropdown}
215186
on:mouseleave={() => searchIndex = null}>
216187
{#each searchResults as searchTask, index}
217-
{@const filepath = _displayableFilePath(searchTask.taskLocation.path)}
188+
{@const filepath = displayPath(searchTask.taskLocation.path)}
218189
<!-- svelte-ignore a11y-click-events-have-key-events -->
219190
<li on:mousedown={() => addTask(searchTask)}
220191
class:selected={search !== null && index === searchIndex}
@@ -234,13 +205,17 @@
234205
</ul>
235206
{/if}
236207
<div class="chip-container results">
237-
{#each existingTasks as task}
208+
{#each editableTask[type] as task}
238209
<div class="chip"
239210
on:mouseenter={(e) => showDescriptionTooltip(e.currentTarget, task.descriptionWithoutTags)}>
240211
<span class="chip-name">[{task.status.symbol}] {task.descriptionWithoutTags}</span>
241212

242213
<button on:click={() => removeTask(task)} type="button" class="chip-close">
243-
<svg style="display: block; margin: auto;" xmlns="http://www.w3.org/2000/svg" width="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="4" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-x"><path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg>
214+
<svg style="display: block; margin: auto;" xmlns="http://www.w3.org/2000/svg" width="12"
215+
viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="4" stroke-linecap="round"
216+
stroke-linejoin="round" class="lucide lucide-x">
217+
<path d="M18 6 6 18"/><path d="m6 6 12 12"/>
218+
</svg>
244219
</button>
245220
</div>
246221
{/each}

src/ui/EditTask.svelte

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -588,24 +588,20 @@
588588
/>
589589
</div>
590590

591-
{#if allTasks.length > 0}
591+
{#if allTasks.length > 0 && onMountComplete}
592592
<!-- --------------------------------------------------------------------------- -->
593593
<!-- Blocked By Tasks -->
594594
<!-- --------------------------------------------------------------------------- -->
595595
<label for="start">Blocked B<span class="accesskey">y</span></label>
596-
{#if onMountComplete}
597-
<Dependency type="blockedBy" task={task} editableTask={editableTask} allTasks={allTasks}
598-
_onDescriptionKeyDown={_onDescriptionKeyDown} accesskey={accesskey} />
599-
{/if}
596+
<Dependency type="blockedBy" task={task} editableTask={editableTask} allTasks={allTasks}
597+
_onDescriptionKeyDown={_onDescriptionKeyDown} accesskey={accesskey} />
600598

601599
<!-- --------------------------------------------------------------------------- -->
602600
<!-- Blocking Tasks -->
603601
<!-- --------------------------------------------------------------------------- -->
604602
<label for="start" class="accesskey-first">Blocking</label>
605-
{#if onMountComplete}
606-
<Dependency type="blocking" task={task} editableTask={editableTask} allTasks={allTasks}
607-
_onDescriptionKeyDown={_onDescriptionKeyDown} accesskey={accesskey} />
608-
{/if}
603+
<Dependency type="blocking" task={task} editableTask={editableTask} allTasks={allTasks}
604+
_onDescriptionKeyDown={_onDescriptionKeyDown} accesskey={accesskey} />
609605
{:else}
610606
<div><i>Blocking and blocked by fields are disabled when vault tasks is empty</i></div>
611607
{/if}

0 commit comments

Comments
 (0)