From 23a310f9864e3339b23dac70acc70fa213354446 Mon Sep 17 00:00:00 2001 From: Julien Calixte Date: Sun, 14 Mar 2021 21:11:06 +0100 Subject: [PATCH] :sparkles: (stacked notes) smooth scroll to focused note --- src/constants/note-width.ts | 1 + src/hooks/useNote.hook.ts | 66 +++++++++++++++++++++++++++++--- src/hooks/useNoteOverlay.hook.ts | 2 +- src/hooks/useOverlay.hook.ts | 23 +++++++---- src/styles/app.scss | 1 + 5 files changed, 78 insertions(+), 15 deletions(-) create mode 100644 src/constants/note-width.ts diff --git a/src/constants/note-width.ts b/src/constants/note-width.ts new file mode 100644 index 0000000..85a7bf3 --- /dev/null +++ b/src/constants/note-width.ts @@ -0,0 +1 @@ +export const NOTE_WIDTH = 620 diff --git a/src/hooks/useNote.hook.ts b/src/hooks/useNote.hook.ts index f11b120..0e70096 100644 --- a/src/hooks/useNote.hook.ts +++ b/src/hooks/useNote.hook.ts @@ -1,10 +1,18 @@ import { Ref, ref } from '@vue/reactivity' -import { computed, nextTick, onUnmounted, watch } from '@vue/runtime-core' +import { + computed, + nextTick, + onMounted, + onUnmounted, + watch +} from '@vue/runtime-core' import { useRoute, useRouter } from 'vue-router' import { noteEventBus } from '@/bus/noteBusEvent' import { useLinks } from '@/hooks/useLinks.hook' import { useRepo } from '@/hooks/useRepo.hook' +import { NOTE_WIDTH } from '@/constants/note-width' +import { useOverlay } from '@/hooks/useOverlay.hook' const sanitizePath = (path: string) => { if (path.startsWith('./')) { @@ -13,9 +21,15 @@ const sanitizePath = (path: string) => { return decodeURIComponent(path) } -export const useNote = (user: Ref, repo: Ref) => { +export const useNote = ( + containerClass: string, + user: Ref, + repo: Ref +) => { const { push } = useRouter() const { query } = useRoute() + const { scrollTo } = useOverlay(false) + const stackedNotes = ref( query.stackedNotes ? Array.isArray(query.stackedNotes) @@ -26,19 +40,38 @@ export const useNote = (user: Ref, repo: Ref) => { const { readme, notFound, tree } = useRepo(user, repo) const { listenToClick } = useLinks('note-display') - const titles = computed(() => { - return stackedNotes.value.reduce((obj: Record, note) => { + + const titles = computed(() => + stackedNotes.value.reduce((obj: Record, note) => { if (!note) { return obj } const filePath = tree.value.find((file) => file.sha === note)?.path ?? '' + const fileNames = filePath.split('.') + fileNames.pop() - obj[note] = fileNames.join('.') + obj[note] = fileNames + .join('.') + .split('/') + .filter((path) => !path.includes('README')) + .join('/') return obj }, {}) - }) + ) + + const scrollToFocusedNote = (sha?: string) => { + if (!sha) { + return + } + nextTick(() => { + const index = stackedNotes.value.findIndex((noteSHA) => noteSHA === sha) + const left = index * NOTE_WIDTH + + scrollTo(left) + }) + } const unsubscribe = noteEventBus.addEventBusListener( ({ path, currentNoteSHA }) => { @@ -55,6 +88,7 @@ export const useNote = (user: Ref, repo: Ref) => { const file = tree.value.find((file) => file.path === finalPath) if (!file?.sha || stackedNotes.value.includes(file.sha)) { + scrollToFocusedNote(file?.sha) return } @@ -92,6 +126,8 @@ export const useNote = (user: Ref, repo: Ref) => { }) stackedNotes.value = newStackedNotes + + scrollToFocusedNote(file.sha) } ) @@ -101,10 +137,28 @@ export const useNote = (user: Ref, repo: Ref) => { }) }) + const resizeContainer = () => { + const element = document.querySelector( + `.${containerClass}` + ) as HTMLElement | null + if (!element) { + return + } + element.style.width = `${NOTE_WIDTH * (stackedNotes.value.length + 1)}px` + } + + onMounted(() => { + resizeContainer() + }) + onUnmounted(() => { unsubscribe() }) + watch(stackedNotes, resizeContainer, { + immediate: true + }) + return { titles, readme, diff --git a/src/hooks/useNoteOverlay.hook.ts b/src/hooks/useNoteOverlay.hook.ts index 0456c49..82e69de 100644 --- a/src/hooks/useNoteOverlay.hook.ts +++ b/src/hooks/useNoteOverlay.hook.ts @@ -1,9 +1,9 @@ import { computed, onMounted } from '@vue/runtime-core' import { useOverlay } from '@/hooks/useOverlay.hook' +import { NOTE_WIDTH } from '@/constants/note-width' const BOOKMARK_WIDTH = 2 -const NOTE_WIDTH = 620 export const useNoteOverlay = (className: string, index: number) => { const { x } = useOverlay() diff --git a/src/hooks/useOverlay.hook.ts b/src/hooks/useOverlay.hook.ts index af62c3e..1668051 100644 --- a/src/hooks/useOverlay.hook.ts +++ b/src/hooks/useOverlay.hook.ts @@ -1,15 +1,14 @@ -import { onMounted, ref } from 'vue' +import { ref } from 'vue' import { useEventListener } from '@vueuse/core' -export const useOverlay = () => { +export const useOverlay = (listen = true) => { const x = ref(0) + const body = document.querySelector('body') - onMounted(() => { - const element = document.querySelector('body') - + if (listen) { useEventListener( - element, + body, 'scroll', (e) => { const target = e.target as HTMLElement @@ -20,8 +19,16 @@ export const useOverlay = () => { capture: false } ) - }) + } + + const scrollTo = (to: number) => { + body?.scroll({ + left: to + }) + } + return { - x + x, + scrollTo } } diff --git a/src/styles/app.scss b/src/styles/app.scss index a104946..f57a13b 100644 --- a/src/styles/app.scss +++ b/src/styles/app.scss @@ -14,6 +14,7 @@ html { body { font-family: 'Courier Prime', monospace; height: 100vh; + scroll-behavior: smooth } @media screen and (min-width: 769px) {