Focus on and fold notes by clicking
Also tweaks the look a bit, of course.
This commit is contained in:
parent
37f1b0e9d1
commit
21cd015ada
2 changed files with 52 additions and 21 deletions
|
|
@ -9,8 +9,8 @@ const ui = useUiStore();
|
||||||
<template>
|
<template>
|
||||||
<div class="flex h-screen touch-pan-x touch-pan-y flex-col">
|
<div class="flex h-screen touch-pan-x touch-pan-y flex-col">
|
||||||
<CNavbar />
|
<CNavbar />
|
||||||
<div class="h-full overflow-auto px-2 py-1">
|
<div class="h-full overflow-auto p-1">
|
||||||
<CNote :noteId="ui.anchor" :focusPath="ui.focusPath" />
|
<CNote :noteId="ui.anchor" :path="[]" :focusPath="ui.focusPath" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,20 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useNotesStore } from "@/stores/notes";
|
import { useNotesStore } from "@/stores/notes";
|
||||||
|
import { useUiStore } from "@/stores/ui";
|
||||||
import { RiArrowDownSLine, RiArrowRightSLine } from "@remixicon/vue";
|
import { RiArrowDownSLine, RiArrowRightSLine } from "@remixicon/vue";
|
||||||
import { computed, ref, watchEffect } from "vue";
|
import { computed, ref, watchEffect } from "vue";
|
||||||
|
|
||||||
const { notes } = useNotesStore();
|
const notes = useNotesStore();
|
||||||
|
const ui = useUiStore();
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
noteId?: string;
|
noteId?: string;
|
||||||
focusPath?: number[];
|
path: number[]; // From root to here
|
||||||
|
focusPath?: number[]; // From here to focus
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const note = computed(() =>
|
const note = computed(() =>
|
||||||
props.noteId ? notes.get(props.noteId) : undefined,
|
props.noteId ? notes.notes.get(props.noteId) : undefined,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Our children and their locally unique keys.
|
// Our children and their locally unique keys.
|
||||||
|
|
@ -30,7 +33,6 @@ const children = computed(() => {
|
||||||
|
|
||||||
const open = ref(false);
|
const open = ref(false);
|
||||||
|
|
||||||
// We're the node pointed to by the `focusPath`.
|
|
||||||
const focused = computed(() => props.focusPath?.length === 0);
|
const focused = computed(() => props.focusPath?.length === 0);
|
||||||
|
|
||||||
// We want to set open to true when we're on the focus path, but then it should
|
// We want to set open to true when we're on the focus path, but then it should
|
||||||
|
|
@ -45,24 +47,55 @@ function focusPathFor(index: number): number[] | undefined {
|
||||||
if (index !== props.focusPath[0]) return undefined;
|
if (index !== props.focusPath[0]) return undefined;
|
||||||
return props.focusPath.slice(1);
|
return props.focusPath.slice(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function focusOnThis() {
|
||||||
|
ui.focusPath = props.path.slice();
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleOpen() {
|
||||||
|
if (props.focusPath) {
|
||||||
|
// We're on the focus path, so the situation is one of these cases:
|
||||||
|
//
|
||||||
|
// 1. We're closed and focused, in which case this does nothing.
|
||||||
|
// 2. We're open and focused, in which case this does nothing.
|
||||||
|
// 3. We're open and on the focus path, in which case closing will hide the
|
||||||
|
// actual focus and we should be focused instead.
|
||||||
|
//
|
||||||
|
// In any case, we can just...
|
||||||
|
focusOnThis();
|
||||||
|
}
|
||||||
|
|
||||||
|
open.value = !open.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
function onClick() {
|
||||||
|
if (!focused.value) {
|
||||||
|
focusOnThis();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleOpen();
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="flex flex-col">
|
<div class="flex flex-col">
|
||||||
<div
|
<div
|
||||||
class="flex flex-row gap-1"
|
class="flex flex-row gap-1 pl-1"
|
||||||
:class="focused ? ['bg-neutral-200'] : []"
|
:class="focused ? ['bg-neutral-200'] : ['hover:bg-neutral-100']"
|
||||||
@click="open = !open"
|
@click="onClick"
|
||||||
>
|
>
|
||||||
<!-- Fold/unfold symbol -->
|
<!-- Fold/unfold symbol -->
|
||||||
<div v-if="children.length > 0 && !open" class="flex items-center">
|
<div class="flex items-center">
|
||||||
<RiArrowRightSLine size="16px" />
|
<div
|
||||||
</div>
|
class="rounded"
|
||||||
<div v-else-if="children.length > 0" class="flex items-center">
|
:class="focused ? ['hover:bg-neutral-300'] : ['hover:bg-neutral-200']"
|
||||||
<RiArrowDownSLine size="16px" />
|
@click.stop="toggleOpen()"
|
||||||
</div>
|
>
|
||||||
<div v-else class="flex items-center">
|
<RiArrowRightSLine v-if="children.length > 0 && !open" size="16px" />
|
||||||
<RiArrowRightSLine size="16px" class="text-neutral-400" />
|
<RiArrowDownSLine v-else-if="children.length > 0" size="16px" />
|
||||||
|
<RiArrowRightSLine v-else size="16px" class="text-neutral-400" />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Text -->
|
<!-- Text -->
|
||||||
|
|
@ -71,14 +104,12 @@ function focusPathFor(index: number): number[] | undefined {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Children -->
|
<!-- Children -->
|
||||||
<div
|
<div v-if="open && children.length > 0" class="flex flex-col pl-2">
|
||||||
v-if="open && children.length > 0"
|
|
||||||
class="flex flex-col border-l border-neutral-300 pl-3"
|
|
||||||
>
|
|
||||||
<CNote
|
<CNote
|
||||||
v-for="([noteId, key], index) in children"
|
v-for="([noteId, key], index) in children"
|
||||||
:key
|
:key
|
||||||
:note-id
|
:note-id
|
||||||
|
:path="path.concat(index)"
|
||||||
:focusPath="focusPathFor(index)"
|
:focusPath="focusPathFor(index)"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue