This commit is contained in:
2023-04-27 12:09:14 +02:00
parent 3579a5894a
commit 552cd7269b
15 changed files with 392 additions and 47 deletions

View File

@@ -1,23 +1,55 @@
<script setup lang="ts">
import { ref } from 'vue'
import TopBar from './components/TopBar.vue'
import SideBar from './components/SideBar.vue'
import useNotes from './composables/useNotes'
import Note from './components/Note.vue'
import { activeNote } from './composables/useNotes'
const { notes } = useNotes()
const sideBarCollapsed = ref(false)
const viewModes: ViewMode[] = [
{ name: 'Note', icon: 'card-text' },
{ name: 'List', icon: 'list-task' },
{ name: 'Mindmap', icon: 'diagram-3' },
]
const activeViewMode = ref(viewModes[0])
</script>
<template>
<TopBar />
<div class="container g-3 d-flex h-100" style="padding-top: 50px">
<SideBar class="py-3" />
<div id="main" class="bg-white border-start border-end px-4 py-3">
main
<pre>{{ notes }}</pre>
<TopBar
:side-bar-collapsed="sideBarCollapsed"
@toggle-side-bar="sideBarCollapsed = !sideBarCollapsed"
/>
<div class="container g-3 position-relative d-flex h-100">
<SideBar
:view-modes="viewModes"
:active-view-mode="activeViewMode"
@set-view-mode="(viewMode) => (activeViewMode = viewMode)"
class="py-3"
style="margin-top: 50px"
/>
<div
id="main"
class="bg-white border-start border-end px-4 py-3"
:class="{ 'side-bar-collapsed': sideBarCollapsed }"
>
<Note v-if="activeNote" :note="activeNote" />
</div>
</div>
</template>
<style>
<style lang="scss">
#main {
overflow: auto;
flex: 1 1 0;
}</style>
position: absolute;
top: 50px;
left: 0;
right: 0;
bottom: 0;
margin-left: $sidebar-width;
transition: margin $transition-duration ease-out;
&.side-bar-collapsed {
margin-left: 0;
}
}
</style>

View File

@@ -0,0 +1,4 @@
$primary: #1e4bc4;
$white: #fff !default;
$transition-duration: 0.2s;
$sidebar-width: 220px;

View File

@@ -1,5 +1,4 @@
$primary: #1e4bc4;
@import './_variables';
@import 'bootstrap/scss/bootstrap';
body,
@@ -7,3 +6,7 @@ body > div,
html {
height: 100%;
}
body {
overflow-y: scroll;
}

View File

@@ -0,0 +1,98 @@
<script setup lang="ts">
const props = defineProps<{
sideBarCollapsed: boolean
}>()
const emit = defineEmits<{
(e: 'toggleSideBar'): void
}>()
</script>
<template>
<button
class="hamburger hamburger--spin-r"
:class="{ 'is-active': !props.sideBarCollapsed }"
type="button"
@click="emit('toggleSideBar')"
>
<span class="hamburger-box">
<span class="hamburger-inner"></span>
</span>
</button>
</template>
<style scoped lang="scss">
$hamburger-layer-color: $white;
$hamburger-layer-width: 25px;
$hamburger-layer-height: 3px;
$hamburger-layer-spacing: 3px;
$hamburger-padding-x: 0px;
$hamburger-padding-y: 0px;
$hamburger-scale-speed: calc($transition-duration/.22s);
@import 'hamburgers/_sass/hamburgers/hamburgers.scss';
@if index($hamburger-types, spin-r) {
/*
* Spin Reverse
*/
.hamburger--spin-r {
.hamburger-inner {
transition-duration: ($hamburger-scale-speed * 0.22s);
transition-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19);
&::before {
transition: top
($hamburger-scale-speed * 0.1s)
($hamburger-scale-speed * 0.25s)
ease-in,
opacity ($hamburger-scale-speed * 0.1s) ease-in;
}
&::after {
transition: bottom
($hamburger-scale-speed * 0.1s)
($hamburger-scale-speed * 0.25s)
ease-in,
transform
($hamburger-scale-speed * 0.22s)
cubic-bezier(0.55, 0.055, 0.675, 0.19);
}
}
&.is-active {
.hamburger-inner {
transform: rotate(-225deg);
transition-delay: ($hamburger-scale-speed * 0.12s);
transition-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
&::before {
top: 0;
opacity: 0;
transition: top ($hamburger-scale-speed * 0.1s) ease-out,
opacity
($hamburger-scale-speed * 0.1s)
($hamburger-scale-speed * 0.12s)
ease-out;
}
&::after {
bottom: 0;
transform: rotate(90deg);
transition: bottom ($hamburger-scale-speed * 0.1s) ease-out,
transform
($hamburger-scale-speed * 0.22s)
($hamburger-scale-speed * 0.12s)
cubic-bezier(0.215, 0.61, 0.355, 1);
}
}
}
}
}
button.hamburger {
outline: none !important;
line-height: 1;
opacity: 0.9;
border-radius: 2rem;
}
.hamburger-box {
display: block;
}
</style>

9
src/components/Note.vue Normal file
View File

@@ -0,0 +1,9 @@
<script setup lang="ts">
defineProps<{
note: Note
}>()
</script>
<template>
<h1>{{ note.title }}</h1>
<div>{{ note.content }}</div>
</template>

View File

@@ -1,35 +1,35 @@
<script setup lang="ts">
import useNotes from '../composables/useNotes'
import { rootNote } from '../composables/useNotes'
import SideBarMenu from './SideBar/SideBarMenu.vue'
import SideBarMenuItem from './SideBar/SideBarMenuItem.vue'
const { rootNote } = useNotes()
interface ViewMode {
name: string
icon: string
}
const viewModes: ViewMode[] = [
{ name: 'Note', icon: 'card-text' },
{ name: 'List', icon: 'list-task' },
{ name: 'Mindmap', icon: 'diagram-3' },
]
const props = defineProps<{
viewModes: ViewMode[]
activeViewMode: ViewMode
}>()
const emit = defineEmits<{
(e: 'setViewMode', viewMode: ViewMode): void
}>()
</script>
<template>
<div id="sidebar" class="position-relative pe-3 d-flex flex-column" style="width: 200px">
<div id="sidebar" class="position-relative pe-3 d-flex flex-column">
<SideBarMenu>
<template #header>Root note</template>
<template #items>
<SideBarMenuItem icon="house">{{
rootNote?.title
}}</SideBarMenuItem>
<SideBarMenuItem icon="house">{{ rootNote?.title }}</SideBarMenuItem>
</template>
</SideBarMenu>
<SideBarMenu>
<template #header>View mode</template>
<template #items>
<SideBarMenuItem v-for="viewMode in viewModes" :icon="viewMode.icon">{{
viewMode.name
}}</SideBarMenuItem>
<SideBarMenuItem
v-for="viewMode in props.viewModes"
:icon="viewMode.icon"
:active="viewMode.name === activeViewMode.name"
@click="emit('setViewMode', viewMode)"
>{{ viewMode.name }}</SideBarMenuItem
>
</template>
</SideBarMenu>
<SideBarMenu>
@@ -44,5 +44,6 @@ const viewModes: ViewMode[] = [
#sidebar {
gap: 1rem;
overflow-y: auto;
width: $sidebar-width;
}
</style>

View File

@@ -1,10 +1,14 @@
<script setup lang="ts">
const props = defineProps<{
icon?: string
active?: boolean
}>()
</script>
<template>
<a class="text-opacity-50 text-decoration-none w-100 d-block link-secondary">
<a
class="text-opacity-50 text-decoration-none w-100 d-block"
:class="props.active ? 'link-primary fw-bolder' : 'link-secondary'"
>
<i :class="`bi bi-${props.icon}`" class="me-2" v-if="props.icon"></i
><slot></slot>
</a>

View File

@@ -1,10 +1,30 @@
<script setup lang="ts">
import Hamburger from './Hamburger.vue'
const props = defineProps<{
sideBarCollapsed: boolean
}>()
const emit = defineEmits<{
(e: 'toggleSideBar'): void
}>()
</script>
<template>
<div
id="topbar"
class="bg-primary position-absolute top-0 start-0 end-0"
style="height: 50px"
>
<div class="container g-3 h-100 d-flex align-items-center text-white">
top bar
<Hamburger
class="me-3"
:side-bar-collapsed="props.sideBarCollapsed"
@toggle-side-bar="emit('toggleSideBar')"
/><span>Contexted</span>
</div>
</div>
</template>
</template>
<style scoped>
#topbar {
z-index: 500;
}
</style>

View File

@@ -1,15 +1,30 @@
import { ref } from 'vue'
const notes = ref<Note[]>([])
import { ref, computed, watch } from 'vue'
import { useTitle } from '@vueuse/core'
export default function useNotes() {
const setDefaultNotes = (defaultNotes: Note[]) => {
notes.value = defaultNotes.map((note) => ({
export const notes = ref<Note[]>([])
export const activeNote = ref<Note>()
watch(activeNote, () => {
if (activeNote.value) useTitle(`${activeNote.value.title} | Contexted`)
})
export const rootNote = computed<Note | undefined>(() =>
notes.value.find((note: Note) => note.isRoot)
)
watch(
rootNote,
() => {
if (rootNote.value) activeNote.value = rootNote.value
},
{ immediate: true }
)
export const setDefaultNotes = (defaultNotes: Note[]) => {
notes.value = defaultNotes.map(
(note): Note => ({
...note,
created: new Date().getTime(),
modified: new Date().getTime(),
}))
}
const rootNote = notes.value.find((note) => note.isRoot)
return { notes, setDefaultNotes, rootNote }
})
)
}

6
src/global.d.ts vendored
View File

@@ -11,7 +11,11 @@ declare global {
to: string[]
from: string[]
}
isRoot?: boolean
}
interface ViewMode {
name: string
icon: string
}
}
export {}

View File

@@ -2,9 +2,8 @@ import { createApp } from 'vue'
import './assets/style.scss'
import 'bootstrap-icons/font/bootstrap-icons.css'
import App from './App.vue'
import useNotes from './composables/useNotes'
import { setDefaultNotes } from './composables/useNotes'
import { defaultNotes } from './utils/defaultNotes'
const { setDefaultNotes } = useNotes()
setDefaultNotes(defaultNotes)
createApp(App).mount('#app')