Implement controls for new UI state
This commit is contained in:
parent
2f9b1925dc
commit
6e96ae7fd4
5 changed files with 113 additions and 22 deletions
|
|
@ -9,7 +9,12 @@ const ui = useUiStore();
|
|||
window.addEventListener("keypress", (ev) => {
|
||||
if (document.activeElement !== document.body) return;
|
||||
|
||||
if (ev.key === "Escape" && ui.mode === "focus") {
|
||||
if (ev.key === "Escape") {
|
||||
if (ui.mode !== "focus") {
|
||||
ui.focus();
|
||||
return;
|
||||
}
|
||||
|
||||
const parent = ui.focusPath.parent();
|
||||
if (parent) ui.focusOn(parent);
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -3,10 +3,12 @@ import { Path, Segment } from "@/lib/path";
|
|||
import { useNotesStore } from "@/stores/notes";
|
||||
import { useUiStore } from "@/stores/ui";
|
||||
import {
|
||||
RiAddLine,
|
||||
RiArrowDownSLine,
|
||||
RiArrowDownWideLine,
|
||||
RiArrowRightDoubleLine,
|
||||
RiArrowRightSLine,
|
||||
RiArrowUpWideLine,
|
||||
RiCornerDownRightLine,
|
||||
RiCornerUpRightLine,
|
||||
RiEditLine,
|
||||
RiPushpinFill,
|
||||
|
|
@ -14,6 +16,7 @@ import {
|
|||
} from "@remixicon/vue";
|
||||
import { computed, ref, watchEffect } from "vue";
|
||||
import CNoteButton from "./CNoteButton.vue";
|
||||
import CNoteChildEditor from "./CNoteChildEditor.vue";
|
||||
import CNoteEditor from "./CNoteEditor.vue";
|
||||
|
||||
const notes = useNotesStore();
|
||||
|
|
@ -23,11 +26,13 @@ const {
|
|||
path,
|
||||
segment,
|
||||
parentId,
|
||||
parentIndex = 0,
|
||||
forceOpen = false,
|
||||
} = defineProps<{
|
||||
path: Path; // From root to here
|
||||
segment: Segment;
|
||||
parentId?: string;
|
||||
parentIndex?: number;
|
||||
forceOpen?: boolean;
|
||||
}>();
|
||||
|
||||
|
|
@ -54,7 +59,6 @@ const children = computed(() => {
|
|||
});
|
||||
|
||||
const hovering = ref(false);
|
||||
const hover = computed(() => hovering.value && !editing.value);
|
||||
|
||||
const mayOpen = computed(() => children.value.length > 0);
|
||||
const open = computed(() => mayOpen.value && ui.isOpen(path));
|
||||
|
|
@ -78,9 +82,20 @@ function onClick(): void {
|
|||
ui.toggleOpen(path);
|
||||
}
|
||||
|
||||
function onPinButtonClick(): void {
|
||||
if (pinned.value) ui.unsetPinned();
|
||||
else ui.setPinned(segment, parentId);
|
||||
function onInsertSiblingBeforeButtonClick(): void {
|
||||
const parent = path.parent();
|
||||
if (!parent) return;
|
||||
ui.insertAt(parent, parentIndex);
|
||||
}
|
||||
|
||||
function onInsertSiblingAfterButtonClick(): void {
|
||||
const parent = path.parent();
|
||||
if (!parent) return;
|
||||
ui.insertAt(parent, parentIndex + 1);
|
||||
}
|
||||
|
||||
function onInsertChildButtonClick(): void {
|
||||
ui.insertAt(path, children.value.length);
|
||||
}
|
||||
|
||||
function onEditButtonClick(): void {
|
||||
|
|
@ -98,9 +113,29 @@ function onEditEditorFinish(text: string): void {
|
|||
onEditEditorClose();
|
||||
}
|
||||
|
||||
function onPinButtonClick(): void {
|
||||
if (pinned.value) ui.unsetPinned();
|
||||
else ui.setPinned(segment, parentId);
|
||||
}
|
||||
|
||||
function onMoveButtonClick(): void {
|
||||
ui.pushAnchorId(segment.id);
|
||||
}
|
||||
|
||||
function onInsertEditorClose(): void {
|
||||
ui.focus();
|
||||
}
|
||||
|
||||
function onInsertEditorFinish(text: string): void {
|
||||
if (!note.value) return;
|
||||
|
||||
if (insertIndex.value !== undefined) {
|
||||
const childNote = notes.createNote(text);
|
||||
note.value.children.splice(insertIndex.value, 0, childNote.id);
|
||||
}
|
||||
|
||||
onInsertEditorClose();
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
|
@ -114,7 +149,7 @@ function onMoveButtonClick(): void {
|
|||
|
||||
<div
|
||||
class="relative flex min-h-6 pl-1"
|
||||
:class="focused ? 'bg-neutral-200' : hover ? 'bg-neutral-100' : ''"
|
||||
:class="focused ? 'bg-neutral-200' : hovering ? 'bg-neutral-100' : ''"
|
||||
@mouseenter="hovering = true"
|
||||
@mouseleave="hovering = false"
|
||||
@click="onClick"
|
||||
|
|
@ -148,16 +183,41 @@ function onMoveButtonClick(): void {
|
|||
<div v-else class="px-1 font-light italic">note not found</div>
|
||||
|
||||
<!-- Controls -->
|
||||
<div class="absolute right-0 flex h-6 items-center gap-0.5">
|
||||
<CNoteButton :visible="hover" @click.stop="onEditButtonClick">
|
||||
<div v-show="!editing" class="absolute right-0 flex h-6 items-center gap-0.5">
|
||||
<!-- Maybe this should be two separate fullsize buttons so they're easier to click. -->
|
||||
<!-- Maybe I should reorder/group the buttons, especially once I add a delete button. -->
|
||||
<div class="flex h-5 w-5 flex-col">
|
||||
<button
|
||||
class="flex h-0 grow select-none items-center justify-center rounded-t-sm border border-b-0 border-black bg-white p-0.5 text-black transition hover:bg-neutral-200 active:scale-95"
|
||||
:class="{ invisible: !hovering }"
|
||||
@click.stop="onInsertSiblingBeforeButtonClick"
|
||||
>
|
||||
<RiArrowUpWideLine size="16px" />
|
||||
</button>
|
||||
<button
|
||||
class="flex h-0 grow select-none items-center justify-center rounded-b-sm border border-t-0 border-black bg-white p-0.5 text-black transition hover:bg-neutral-200 active:scale-95"
|
||||
:class="{ invisible: !hovering }"
|
||||
@click.stop="onInsertSiblingAfterButtonClick"
|
||||
>
|
||||
<RiArrowDownWideLine size="16px" />
|
||||
</button>
|
||||
</div>
|
||||
<CNoteButton :visible="hovering" @click.stop="onInsertChildButtonClick">
|
||||
<RiCornerDownRightLine size="16px" />
|
||||
</CNoteButton>
|
||||
<CNoteButton :visible="hovering" @click.stop="onEditButtonClick">
|
||||
<RiEditLine size="16px" />
|
||||
</CNoteButton>
|
||||
<CNoteButton :visible="hover || pinned" :inverted="pinned" @click.stop="onPinButtonClick">
|
||||
<CNoteButton
|
||||
:visible="hovering || pinned"
|
||||
:inverted="pinned"
|
||||
@click.stop="onPinButtonClick"
|
||||
>
|
||||
<RiPushpinFill v-if="pinned" size="16px" />
|
||||
<RiPushpinLine v-else size="16px" />
|
||||
</CNoteButton>
|
||||
<CNoteButton
|
||||
:visible="hover"
|
||||
:visible="hovering"
|
||||
:disabled="ui.anchorId === segment.id"
|
||||
@click.stop="onMoveButtonClick"
|
||||
>
|
||||
|
|
@ -167,19 +227,21 @@ function onMoveButtonClick(): void {
|
|||
</div>
|
||||
|
||||
<!-- Children -->
|
||||
<div v-if="open && children.length > 0" class="flex flex-col pl-2">
|
||||
<div v-if="insertIndex === 0" class="flex items-start pl-3">
|
||||
<div class="flex h-6 items-center"><RiAddLine size="16px" /></div>
|
||||
<CNoteEditor class="flex-1" />
|
||||
</div>
|
||||
<div v-if="open || insertIndex !== undefined" class="flex flex-col pl-2">
|
||||
<CNoteChildEditor
|
||||
v-if="insertIndex === 0"
|
||||
@close="onInsertEditorClose"
|
||||
@finish="onInsertEditorFinish"
|
||||
/>
|
||||
|
||||
<template v-for="(child, index) of children" :key="child.fmt()">
|
||||
<CNote :path="path.concat(child)" :segment="child" :parent-id="id" />
|
||||
<CNote :path="path.concat(child)" :segment="child" :parent-id="id" :parent-index="index" />
|
||||
|
||||
<div v-if="insertIndex === index + 1" class="flex items-start pl-3">
|
||||
<div class="flex h-6 items-center"><RiAddLine size="16px" /></div>
|
||||
<CNoteEditor class="flex-1" />
|
||||
</div>
|
||||
<CNoteChildEditor
|
||||
v-if="insertIndex === index + 1"
|
||||
@close="onInsertEditorClose"
|
||||
@finish="onInsertEditorFinish"
|
||||
/>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ const {
|
|||
|
||||
<template>
|
||||
<button
|
||||
class="flex select-none items-center rounded-sm border border-black p-0.5 transition"
|
||||
class="flex h-5 w-5 select-none items-center justify-center rounded-sm border border-black transition"
|
||||
:class="{
|
||||
'bg-white text-black': !inverted,
|
||||
'bg-black text-white': inverted,
|
||||
|
|
|
|||
23
gdn-app/src/components/CNoteChildEditor.vue
Normal file
23
gdn-app/src/components/CNoteChildEditor.vue
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
<script setup lang="ts">
|
||||
import { RiAddLine } from "@remixicon/vue";
|
||||
import CNoteEditor from "./CNoteEditor.vue";
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: "close"): void;
|
||||
(e: "finish", text: string): void;
|
||||
}>();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex items-start pl-1">
|
||||
<div class="flex h-6 items-center">
|
||||
<RiAddLine size="16px" />
|
||||
</div>
|
||||
|
||||
<CNoteEditor
|
||||
class="flex-1"
|
||||
@close="() => emit('close')"
|
||||
@finish="(text) => emit('finish', text)"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -14,6 +14,7 @@ const emit = defineEmits<{
|
|||
|
||||
const textarea = useTemplateRef<HTMLTextAreaElement>("textarea");
|
||||
const text = ref(initialText);
|
||||
// TODO Store text globally somewhere so it doesn't get lost when editor moves
|
||||
|
||||
onMounted(() => {
|
||||
textarea.value?.focus();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue