Skip to content

Commit 52323a6

Browse files
committed
Update treetop project to Svelte 5
1 parent dbbc073 commit 52323a6

11 files changed

+233
-237
lines changed

src/treetop/Bookmark.svelte

Lines changed: 34 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,13 @@
77
import type * as Treetop from './types';
88
import { truncateMiddle } from './utils';
99
10-
export let nodeId: string;
11-
export let title: string;
12-
export let url: string;
10+
interface Props {
11+
nodeId: string;
12+
title: string;
13+
url: string;
14+
}
15+
16+
let { nodeId, title, url }: Props = $props();
1317
1418
const lastVisitTimeMap: Treetop.LastVisitTimeMap =
1519
getContext('lastVisitTimeMap');
@@ -21,30 +25,22 @@
2125
// Maximum length of displayed bookmark titles and, in tooltips, URLs.
2226
const maxLength = 78;
2327
24-
let name: string;
25-
$: {
26-
// Set name, truncating based on preference setting.
27-
// Fall back to URL if title is blank.
28-
if ($truncate) {
29-
name = lodashTruncate(title || url, {
30-
length: maxLength,
31-
separator: ' ',
32-
});
33-
} else {
34-
name = title || url;
35-
}
36-
}
37-
38-
let tooltip: string | undefined;
39-
$: {
40-
// Set tooltip if preference is enabled.
41-
// Display title and URL on separate lines, truncating long URLs in the middle.
42-
if ($tooltips) {
43-
tooltip = `${title}\n${truncateMiddle(url, maxLength)}`;
44-
} else {
45-
tooltip = undefined;
46-
}
47-
}
28+
// Set name, truncating based on preference setting.
29+
// Fall back to URL if title is blank.
30+
const name = $derived(
31+
$truncate
32+
? lodashTruncate(title || url, {
33+
length: maxLength,
34+
separator: ' ',
35+
})
36+
: title || url,
37+
);
38+
39+
// Set tooltip if preference is enabled.
40+
// Display title and URL on separate lines, truncating long URLs in the middle.
41+
const tooltip = $derived(
42+
$tooltips ? `${title}\n${truncateMiddle(url, maxLength)}` : undefined,
43+
);
4844
4945
// Number of milliseconds in a day
5046
const MILLISECONDS_PER_DAY =
@@ -54,25 +50,18 @@
5450
24; // hours per day
5551
5652
// Approximate number of days since last visit
57-
let lastVisitTimeDays: number;
58-
$: lastVisitTimeDays =
59-
($clock.getTime() - $lastVisitTime) / MILLISECONDS_PER_DAY;
53+
const lastVisitTimeDays = $derived(
54+
($clock.getTime() - $lastVisitTime) / MILLISECONDS_PER_DAY,
55+
);
6056
6157
// Set visited class based on number of days since last visit
62-
let visitedClass: string;
63-
$: {
64-
if (lastVisitTimeDays < 1) {
65-
visitedClass = 'visitedPastDay';
66-
} else if (lastVisitTimeDays < 2) {
67-
visitedClass = 'visitedPastTwoDays';
68-
} else if (lastVisitTimeDays < 3) {
69-
visitedClass = 'visitedPastThreeDays';
70-
} else if (lastVisitTimeDays < 7) {
71-
visitedClass = 'visitedPastWeek';
72-
} else {
73-
visitedClass = '';
74-
}
75-
}
58+
const visitedClass = $derived.by(() => {
59+
if (lastVisitTimeDays < 1) return 'visitedPastDay';
60+
if (lastVisitTimeDays < 2) return 'visitedPastTwoDays';
61+
if (lastVisitTimeDays < 3) return 'visitedPastThreeDays';
62+
if (lastVisitTimeDays < 7) return 'visitedPastWeek';
63+
return '';
64+
});
7665
</script>
7766

7867
<style>
@@ -108,6 +97,7 @@
10897
text-decoration-line: underline;
10998
text-decoration-style: solid;
11099
text-decoration-thickness: 1px;
100+
text-underline-offset: 0.1em;
111101
}
112102
113103
a:hover {

src/treetop/ConfirmationDialog.svelte

Lines changed: 29 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,39 @@
11
<script lang="ts">
2-
import { createEventDispatcher } from 'svelte';
32
import type { MDCDialogCloseEvent } from '@material/dialog';
43
import Button, { Label } from '@smui/button';
54
import Dialog, { Actions, Content, InitialFocus, Title } from '@smui/dialog';
65
7-
const dispatch = createEventDispatcher<{
8-
confirm: null;
9-
cancel: null;
10-
}>();
6+
interface Props {
7+
open?: boolean;
8+
title: string;
9+
message: string;
10+
cancelLabel: string;
11+
confirmLabel: string;
12+
onConfirm: () => void;
13+
onCancel: () => void;
14+
}
1115
12-
export let open = false;
13-
export let title: string;
14-
export let message: string;
15-
export let cancelLabel: string;
16-
export let confirmLabel: string;
16+
let {
17+
open = $bindable(false),
18+
title,
19+
message,
20+
cancelLabel,
21+
confirmLabel,
22+
onConfirm,
23+
onCancel,
24+
}: Props = $props();
1725
1826
function handleClosed(e: MDCDialogCloseEvent) {
19-
const message = e.detail.action === 'confirm' ? 'confirm' : 'cancel';
20-
dispatch(message);
27+
switch (e.detail.action) {
28+
case 'confirm':
29+
onConfirm();
30+
break;
31+
case 'cancel':
32+
onCancel();
33+
break;
34+
default:
35+
break;
36+
}
2137
}
2238
</script>
2339

@@ -26,7 +42,7 @@
2642
class="treetopDialog"
2743
aria-labelledby="dialog-title"
2844
aria-describedby="dialog-content"
29-
on:SMUIDialog:closed={handleClosed}>
45+
onSMUIDialogClosed={handleClosed}>
3046
<Title id="dialog-title">{title}</Title>
3147
<Content id="dialog-content">{message}</Content>
3248
<Actions>

src/treetop/FilterInput.svelte

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,36 @@
11
<script lang="ts">
22
/* eslint-disable simple-import-sort/imports */
33
4-
import { createEventDispatcher, onMount } from 'svelte';
4+
import { onMount } from 'svelte';
55
import TextField from '@smui/textfield';
66
// FIXME: IconButton must appear after TextField, otherwise custom
77
// TextField styles aren't applied correctly
88
import IconButton from '@smui/icon-button';
99
import debounce from 'lodash-es/debounce';
1010
11+
interface Props {
12+
onInput: (filter: string) => void;
13+
}
14+
15+
let { onInput }: Props = $props();
16+
1117
// Input
1218
let input: TextField;
1319
1420
// Filter string
15-
let filter = '';
21+
let filter = $state('');
1622
1723
// Icon button
1824
let iconButton: IconButton;
1925
2026
// Debounce duration for typing in filter input (ms)
2127
const FILTER_DEBOUNCE_MS = 275;
2228
23-
const dispatch = createEventDispatcher<{ input: { filter: string } }>();
24-
2529
// CSS 'visibility' value for the clear button.
2630
// The button is visible only when text is entered.
27-
$: clearIconButtonVisibility = filter.length > 0 ? 'visible' : 'hidden';
31+
const clearIconButtonVisibility = $derived(
32+
filter.length > 0 ? 'visible' : 'hidden',
33+
);
2834
2935
export function focus() {
3036
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
@@ -43,7 +49,7 @@
4349
* Dispatch an input event with the current filter string.
4450
*/
4551
function dispatchInputEvent() {
46-
dispatch('input', { filter });
52+
onInput(filter);
4753
}
4854
4955
/**
@@ -102,20 +108,20 @@
102108
</style>
103109

104110
<div class="searchBox">
105-
<form on:submit={handleFilterSubmit}>
106-
<!-- svelte-ignore missing-declaration -->
111+
<form onsubmit={handleFilterSubmit}>
107112
<TextField
108113
bind:this={input}
109114
bind:value={filter}
110-
on:input={debounceFilterInput}
111-
on:keydown={onKeyDown}
115+
oninput={debounceFilterInput}
116+
onkeydown={onKeyDown}
112117
label={chrome.i18n.getMessage('search')}>
113-
<IconButton
114-
bind:this={iconButton}
115-
class="material-icons"
116-
slot="trailingIcon"
117-
style="visibility: {clearIconButtonVisibility}"
118-
on:click={clearFilterInput}>clear</IconButton>
118+
{#snippet trailingIcon()}
119+
<IconButton
120+
bind:this={iconButton}
121+
class="material-icons"
122+
style="visibility: {clearIconButtonVisibility}"
123+
onclick={clearFilterInput}>clear</IconButton>
124+
{/snippet}
119125
</TextField>
120126
</form>
121127
</div>

0 commit comments

Comments
 (0)