(stacked notes) implements overlay

This commit is contained in:
2021-03-14 15:21:14 +01:00
parent cd381f9c4a
commit 698c865b39
6 changed files with 172 additions and 45 deletions

View File

@@ -1,22 +1,35 @@
<template> <template>
<div class="stacked-note" v-html="content"></div> <div
class="stacked-note"
:class="{ [className]: true, overlay: displayNoteOverlay }"
>
<div class="title-stacked-note" :class="titleClassName">{{ title }}</div>
<section v-html="content"></section>
</div>
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent, nextTick, watch } from 'vue' import { computed, defineComponent, nextTick, watch } from 'vue'
import { useFile } from '@/hooks/useFile.hook' import { useFile } from '@/hooks/useFile.hook'
import { useLinks } from '@/hooks/useLinks.hook' import { useLinks } from '@/hooks/useLinks.hook'
import { useNoteOverlay } from '@/hooks/useNoteOverlay.hook'
export default defineComponent({ export default defineComponent({
name: 'StackedNote', name: 'StackedNote',
props: { props: {
index: { type: Number, required: true },
user: { type: String, required: true }, user: { type: String, required: true },
repo: { type: String, required: true }, repo: { type: String, required: true },
title: { type: String, required: true },
sha: { type: String, required: true } sha: { type: String, required: true }
}, },
setup(props) { setup(props) {
const { content } = useFile(props.user, props.repo, props.sha) const { content } = useFile(props.user, props.repo, props.sha)
const { listenToClick } = useLinks('stacked-note', props.sha) const { listenToClick } = useLinks('stacked-note', props.sha)
const className = computed(() => `stacked-note-${props.index}`)
const titleClassName = computed(() => `title-${className.value}`)
const { displayNoteOverlay } = useNoteOverlay(className.value, props.index)
watch(content, () => { watch(content, () => {
if (content.value) { if (content.value) {
@@ -26,24 +39,43 @@ export default defineComponent({
} }
}) })
return { content } return { content, titleClassName, className, displayNoteOverlay }
} }
}) })
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
$border-color: rgba(18, 19, 58, 0.2);
.stacked-note { .stacked-note {
text-align: left; padding: 1rem 3rem;
border-top: 1px solid rgba(18, 19, 58, 0.2);
padding: 0 1rem; transition: cubic-bezier(0.39, 0.575, 0.565, 1) 0.3s;
overflow-y: auto;
height: 100vh; &.overlay {
box-shadow: -3px 0 0.4em $border-color;
}
}
.title-stacked-note {
position: absolute;
transform-origin: 0 0;
transform: rotate(90deg);
top: 1rem;
left: 1.5rem;
direction: rtl;
}
@media screen and (max-width: 768px) {
.title-stacked-note {
display: none;
}
} }
@media screen and (min-width: 769px) { @media screen and (min-width: 769px) {
.stacked-note { .stacked-note {
border-top: 0; border-top: 0;
border-left: 1px solid rgba(18, 19, 58, 0.2); border-left: 1px solid $border-color;
} }
} }
</style> </style>

View File

@@ -1,5 +1,5 @@
import { Ref, ref } from '@vue/reactivity' import { Ref, ref } from '@vue/reactivity'
import { nextTick, onUnmounted, watch } from '@vue/runtime-core' import { computed, nextTick, onUnmounted, watch } from '@vue/runtime-core'
import { useRoute, useRouter } from 'vue-router' import { useRoute, useRouter } from 'vue-router'
import { noteEventBus } from '@/bus/noteBusEvent' import { noteEventBus } from '@/bus/noteBusEvent'
@@ -26,6 +26,19 @@ export const useNote = (user: Ref<string>, repo: Ref<string>) => {
const { readme, notFound, tree } = useRepo(user, repo) const { readme, notFound, tree } = useRepo(user, repo)
const { listenToClick } = useLinks('note-display') const { listenToClick } = useLinks('note-display')
const titles = computed(() => {
return stackedNotes.value.reduce((obj: Record<string, string>, 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('.')
return obj
}, {})
})
const unsubscribe = noteEventBus.addEventBusListener( const unsubscribe = noteEventBus.addEventBusListener(
({ path, currentNoteSHA }) => { ({ path, currentNoteSHA }) => {
@@ -93,6 +106,7 @@ export const useNote = (user: Ref<string>, repo: Ref<string>) => {
}) })
return { return {
titles,
readme, readme,
notFound, notFound,
stackedNotes stackedNotes

View File

@@ -0,0 +1,27 @@
import { computed, onMounted } from '@vue/runtime-core'
import { useOverlay } from '@/hooks/useOverlay.hook'
const BOOKMARK_WIDTH = 2
const NOTE_WIDTH = 620
export const useNoteOverlay = (className: string, index: number) => {
const { x } = useOverlay()
const displayNoteOverlay = computed(() => x.value > index * NOTE_WIDTH)
onMounted(() => {
const noteElement = document.querySelector(
`.${className}`
) as HTMLElement | null
if (!noteElement) {
return
}
noteElement.style.left = `${(index + 1) * BOOKMARK_WIDTH}rem`
})
return {
displayNoteOverlay
}
}

View File

@@ -0,0 +1,27 @@
import { onMounted, ref } from 'vue'
import { useEventListener } from '@vueuse/core'
export const useOverlay = () => {
const x = ref(0)
onMounted(() => {
const element = document.querySelector('body')
useEventListener(
element,
'scroll',
(e) => {
const target = e.target as HTMLElement
x.value = target.scrollLeft
},
{
passive: true,
capture: false
}
)
})
return {
x
}
}

View File

@@ -4,12 +4,13 @@
html { html {
overflow-y: auto; overflow-y: auto;
overflow-x: auto;
} }
body { body {
font-family: 'Courier Prime', monospace; font-family: 'Courier Prime', monospace;
text-align: center;
height: 100vh; height: 100vh;
// width: 5000px;
} }
@media screen and (min-width: 769px) { @media screen and (min-width: 769px) {

View File

@@ -27,15 +27,16 @@
</button> </button>
</form> </form>
</div> </div>
<div class="home content" v-else> <div v-else-if="notFound">
<hr v-if="notFound" /> <hr />
<div v-if="notFound" class="columns is-centered"> <div class="columns is-centered">
<div class="column is-one-third notification is-warning" v-if="notFound"> <div class="column is-one-third notification is-warning" v-if="notFound">
Not found. Not found.
</div> </div>
</div> </div>
<div class="note-container"> </div>
<div class="readme"> <div class="home content note-container" v-else>
<div class="readme note">
<h1 class="title is-1"> <h1 class="title is-1">
<router-link <router-link
:to="{ name: 'Home', params: { user, repo } }" :to="{ name: 'Home', params: { user, repo } }"
@@ -49,14 +50,15 @@
</div> </div>
<stacked-note <stacked-note
class="note" class="note"
v-for="stackedNote in stackedNotes" v-for="(stackedNote, index) in stackedNotes"
:key="stackedNote" :key="stackedNote"
:index="index"
:user="user" :user="user"
:repo="repo" :repo="repo"
:sha="stackedNote" :sha="stackedNote"
:title="titles[stackedNote]"
/> />
</div> </div>
</div>
</template> </template>
<script lang="ts"> <script lang="ts">
@@ -79,6 +81,7 @@ export default defineComponent({
}, },
setup(props) { setup(props) {
const refProps = toRefs(props) const refProps = toRefs(props)
return { return {
...useNote(refProps.user, refProps.repo), ...useNote(refProps.user, refProps.repo),
...useForm(), ...useForm(),
@@ -91,20 +94,43 @@ export default defineComponent({
<style lang="scss" scoped> <style lang="scss" scoped>
.home { .home {
display: flex; display: flex;
flex-direction: column; width: 100%;
.readme {
position: sticky;
left: 0;
padding: 0 2rem 1rem;
}
.note {
text-align: left;
overflow-y: auto;
height: 100vh;
position: sticky;
background-color: #fff;
&:not(:first-child) {
border-top: 1px solid rgba(18, 19, 58, 0.2);
}
}
@media screen and (min-width: 769px) { @media screen and (min-width: 769px) {
.readme,
.note { .note {
min-width: 620px; min-width: 620px;
max-width: 720px; max-width: 620px;
} }
} }
}
.note-container { @media screen and (max-width: 768px) {
flex: 1; .home {
display: flex; flex-wrap: wrap;
overflow-x: auto;
.note {
position: initial;
width: 100vw;
height: auto;
}
} }
} }
</style> </style>