Skip to content

Commit 6f1e0fa

Browse files
Zooming
1 parent 0c3fb75 commit 6f1e0fa

File tree

4 files changed

+113
-56
lines changed

4 files changed

+113
-56
lines changed

src/App.svelte

Lines changed: 41 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,20 @@
6161
itemStore.paste($zoomedItemId)
6262
}
6363
64+
if ((event.metaKey || event.ctrlKey) && event.key === 'a') {
65+
if (!event.target.closest('[contenteditable]')) {
66+
event.preventDefault()
67+
const currentRoot = $zoomedItem || $filteredItems.tree
68+
const flat = flattenVisibleTree(currentRoot)
69+
const allIds = flat.map(item => item.id)
70+
if (allIds.length > 0) {
71+
itemStore.selectRange(allIds)
72+
itemStore.setSelectionAnchor(allIds[0])
73+
itemStore.setSelectionHead(allIds[allIds.length - 1])
74+
}
75+
}
76+
}
77+
6478
if ((event.metaKey || event.ctrlKey) && (event.key === 'Delete' || event.key === 'Backspace')) {
6579
if ($selection.size > 0 && !event.target.closest('[contenteditable]')) {
6680
event.preventDefault()
@@ -118,12 +132,18 @@
118132
return
119133
}
120134
135+
if ($selection.size > 0) {
136+
itemStore.clearSelection()
137+
return
138+
}
139+
121140
if ($zoomedItemId) {
141+
const previousZoomId = $zoomedItemId
122142
itemStore.zoomOut()
143+
tick().then(() => focusItem(previousZoomId))
123144
return
124145
}
125146
126-
itemStore.clearSelection()
127147
itemStore.clearSearch()
128148
selectionBox = null
129149
}
@@ -223,25 +243,37 @@
223243
if (event.altKey && (event.key === 'ArrowLeft' || event.key === 'ArrowRight')) {
224244
event.preventDefault()
225245
246+
const activeEditor = document.activeElement?.closest('[contenteditable]')
247+
let focusedItemId = null
248+
249+
if (activeEditor) {
250+
const itemElement = activeEditor.closest('.item') || activeEditor.closest('[id^="item_"]')
251+
if (itemElement) {
252+
focusedItemId = itemElement.id.replace('item_', '')
253+
}
254+
}
255+
226256
if (event.key === 'ArrowLeft') {
257+
const previousZoomId = $zoomedItemId
227258
itemStore.zoomOut()
259+
if (previousZoomId) {
260+
tick().then(() => focusItem(previousZoomId))
261+
}
228262
} else {
229-
const activeEditor = document.activeElement?.closest('[contenteditable]')
230-
let targetItemId = null
263+
let targetItemId = focusedItemId
231264
232-
if (activeEditor) {
233-
const itemElement = activeEditor.closest('.item') || activeEditor.closest('[id^="item_"]')
234-
if (itemElement) {
235-
targetItemId = itemElement.id.replace('item_', '')
236-
}
237-
} else if ($selection.size > 0) {
265+
if (!targetItemId && $selection.size > 0) {
238266
targetItemId = [...$selection][0]
239267
}
240268
241269
if (targetItemId) {
242270
itemStore.zoom(targetItemId)
243271
}
244272
}
273+
274+
if (focusedItemId) {
275+
tick().then(() => focusItem(focusedItemId))
276+
}
245277
}
246278
}
247279

src/components/List.svelte

Lines changed: 48 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
let containerElement
2828
let showDescriptionEditor = false
2929
30-
$: isZoomedRoot = isTop && $zoomedItemId && item.id === $zoomedItemId
30+
$: isZoomedRoot = isTop && !!$zoomedItemId && item.id === $zoomedItemId
3131
$: hasDescription = !!item.description?.trim()
3232
3333
function handleNewBullet(event) {
@@ -225,53 +225,57 @@
225225
class:outermost
226226
>
227227
{#if isZoomedRoot}
228-
<h1 class="zoomed_title" id="item_{item.id}">
229-
<RichEditor
230-
bind:value={item.text}
231-
{highlightPhrase}
232-
showPlaceholder={false}
233-
itemId={item.id}
234-
on:selectdown={handleTitleSelectDown}
235-
on:newbullet={handleTitleNewBullet}
236-
on:change={() => itemStore.updateItem(item.id, { text: item.text })}
237-
on:hashtagclick={handleHashtagClick}
238-
on:itemrefclick={handleItemRefClick}
239-
/>
240-
</h1>
241-
242-
{#if hasDescription || showDescriptionEditor}
243-
<div class="zoomed_description">
228+
{#key item.id}
229+
<h1 class="zoomed_title" id="item_{item.id}">
244230
<RichEditor
245-
bind:value={item.description}
246-
isDescription={true}
247-
showPlaceholder={false}
231+
bind:value={item.text}
248232
{highlightPhrase}
249-
editorClass="editable description"
250-
on:change={handleDescriptionChange}
251-
on:delete={handleExitDescription}
252-
on:exitdescription={handleExitDescription}
253-
on:blur={handleDescriptionBlur}
233+
showPlaceholder={false}
234+
itemId={item.id}
235+
on:selectdown={handleTitleSelectDown}
236+
on:newbullet={handleTitleNewBullet}
237+
on:change={() => itemStore.updateItem(item.id, { text: item.text })}
238+
on:hashtagclick={handleHashtagClick}
239+
on:itemrefclick={handleItemRefClick}
254240
/>
255-
</div>
256-
{:else}
257-
<button class="add-description-btn" on:click={handleDescriptionClick}>
258-
Click to add description...
259-
</button>
260-
{/if}
241+
</h1>
242+
243+
{#if hasDescription || showDescriptionEditor}
244+
<div class="zoomed_description">
245+
<RichEditor
246+
bind:value={item.description}
247+
isDescription={true}
248+
showPlaceholder={false}
249+
{highlightPhrase}
250+
editorClass="editable description"
251+
on:change={handleDescriptionChange}
252+
on:delete={handleExitDescription}
253+
on:exitdescription={handleExitDescription}
254+
on:blur={handleDescriptionBlur}
255+
/>
256+
</div>
257+
{:else}
258+
<button class="add-description-btn" on:click={handleDescriptionClick}>
259+
Click to add description...
260+
</button>
261+
{/if}
262+
{/key}
261263
{:else if isTop && item.text?.length && !outermost}
262-
<h2 class="item_title" id="item_{item.id}">
263-
<RichEditor
264-
bind:value={item.text}
265-
{highlightPhrase}
266-
showPlaceholder={false}
267-
itemId={item.id}
268-
on:selectdown={handleTitleSelectDown}
269-
on:newbullet={handleTitleNewBullet}
270-
on:change={() => itemStore.updateItem(item.id, { text: item.text })}
271-
on:hashtagclick={handleHashtagClick}
272-
on:itemrefclick={handleItemRefClick}
273-
/>
274-
</h2>
264+
{#key item.id}
265+
<h2 class="item_title" id="item_{item.id}">
266+
<RichEditor
267+
bind:value={item.text}
268+
{highlightPhrase}
269+
showPlaceholder={false}
270+
itemId={item.id}
271+
on:selectdown={handleTitleSelectDown}
272+
on:newbullet={handleTitleNewBullet}
273+
on:change={() => itemStore.updateItem(item.id, { text: item.text })}
274+
on:hashtagclick={handleHashtagClick}
275+
on:itemrefclick={handleItemRefClick}
276+
/>
277+
</h2>
278+
{/key}
275279
{/if}
276280
277281
{#if item.children?.length && (item.open || isTop)}

src/stores/itemStore.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -532,7 +532,12 @@ function createItemStore() {
532532

533533
const root = get(items)
534534
const parent = findParent(root, current)
535-
zoomedItemId.set(parent?.id || null)
535+
536+
if (!parent || parent.id === root.id) {
537+
zoomedItemId.set(null)
538+
} else {
539+
zoomedItemId.set(parent.id)
540+
}
536541
}
537542

538543
async function navigateToItem(id) {

src/utils/focus.js

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,15 @@ export function focusItem(id) {
1010
itemStore.setSelectionHead(id)
1111

1212
requestAnimationFrame(() => {
13-
const el = document.querySelector(`#item_${id} .title-editor [contenteditable]`)
13+
let el = document.querySelector(`#item_${id} .title-editor [contenteditable]`)
14+
15+
if (!el) {
16+
el = document.querySelector(`#item_${id}[contenteditable]`)
17+
}
18+
if (!el) {
19+
el = document.querySelector(`#item_${id} [contenteditable]`)
20+
}
21+
1422
if (el) {
1523
el.focus()
1624
const range = document.createRange()
@@ -30,7 +38,15 @@ export function focusItemStart(id) {
3038
itemStore.setSelectionHead(id)
3139

3240
requestAnimationFrame(() => {
33-
const el = document.querySelector(`#item_${id} .title-editor [contenteditable]`)
41+
let el = document.querySelector(`#item_${id} .title-editor [contenteditable]`)
42+
43+
if (!el) {
44+
el = document.querySelector(`#item_${id}[contenteditable]`)
45+
}
46+
if (!el) {
47+
el = document.querySelector(`#item_${id} [contenteditable]`)
48+
}
49+
3450
if (el) {
3551
el.focus()
3652
const range = document.createRange()

0 commit comments

Comments
 (0)