This commit is contained in:
2023-05-19 19:12:55 +02:00
parent 35b016449a
commit e802ad289c
4 changed files with 104 additions and 85 deletions

View File

@@ -23,9 +23,7 @@ const emit = defineEmits<{
</template> </template>
<template #default>Are you sure you want to delete this note?</template> <template #default>Are you sure you want to delete this note?</template>
<template #actions="{ close }"> <template #actions="{ close }">
<button class="btn-primary btn-sm btn mr-1" @click="emit('delete', close)"> <button class="btn-primary btn-sm btn" @click="emit('delete', close)">Delete note</button>
Delete note
</button>
<button class="btn-sm btn" @click="close">Close</button> <button class="btn-sm btn" @click="close">Close</button>
</template> </template>
</Modal> </Modal>
@@ -35,12 +33,10 @@ const emit = defineEmits<{
<i class="fas fa-fw fa-sitemap" /> <i class="fas fa-fw fa-sitemap" />
</button> </button>
</template> </template>
<template #default> <template #default>Are you sure you want to set this note as root note?</template>
Are you sure you want to set this note as root note?
</template>
<template #actions="{ close }"> <template #actions="{ close }">
<button class="btn-sm btn" @click="close">Close</button> <button class="btn-sm btn" @click="close">Close</button>
<button class="btn-primary btn-sm btn mr-1" @click="emit('setRoot', close)"> <button class="btn-primary btn-sm btn" @click="emit('setRoot', close)">
Set current note as root Set current note as root
</button> </button>
</template> </template>

View File

@@ -1,70 +1,102 @@
<script setup lang="ts"> <script setup lang="ts">
const rows = [ import { getNoteReferences, setActiveNote, findNotes, deleteNote } from '@/composables/useNotes'
{ import { formatDate } from '@/utils/helpers'
noteTitle: 'Cy Ganderton',
references: 'Quality Control Specialist',
modified: 'Blue'
},
{
noteTitle: 'Hart Hagerty',
references: 'Desktop Support Technician',
modified: 'Purple',
rootNote: true
},
{
noteTitle: 'Brice Swyre',
references: 'Tax Accountant',
modified: 'Red'
}
].map((row) => ({
...row,
selected: false
}))
const checkedRows = ref<{ [key: string]: Boolean }>({}) const notesWithReferences = computed(() => {
return findNotes(filter.value).map((note) => ({
...note,
references: getNoteReferences(note)
}))
})
const toggleRow = (row: any) => { const selectedNotes = ref<{ [key: string]: Boolean }>({})
if (!row.rootNote) checkedRows.value[row.noteTitle] = !checkedRows.value[row.noteTitle] const countSelectedNotes = computed(
() => Object.entries(selectedNotes.value).filter(([, selected]) => Boolean(selected)).length
)
const toggleRow = (note: Note) => {
if (!note.isRoot) selectedNotes.value[note.id] = !selectedNotes.value[note.id]
}
const filter = ref('')
const deleteSelectedNotes = (closeModal: () => void) => {
closeModal()
const notesToDelete = Object.entries(selectedNotes.value)
.filter(([, selected]) => Boolean(selected))
.map(([id]) => id)
notesToDelete.forEach((noteId) => deleteNote(noteId))
selectedNotes.value = {}
} }
</script> </script>
<template> <template>
<div class="overflow-x-auto"> <div class="flex flex-col gap-2 overflow-x-auto">
<div class="flex items-center gap-2">
<div class="flex items-center">
<span class="whitespace-nowrap">
{{ notesWithReferences.length }} {{ notesWithReferences.length === 1 ? 'note' : 'notes' }}
</span>
<template v-if="countSelectedNotes > 0">
<span class="mx-1">|</span>
<div class="whitespace-nowrap font-semibold">{{ countSelectedNotes }} selected</div>
</template>
</div>
<Modal v-if="countSelectedNotes > 0">
<template #activator="{ open }">
<button class="btn-toolbar btn-sm btn" @click="open">Delete</button>
</template>
<template #default>Are you sure you want to delete the selected notes?</template>
<template #actions="{ close }">
<button class="btn-primary btn-sm btn" @click="deleteSelectedNotes(close)">
Delete selected notes
</button>
<button class="btn-sm btn" @click="close">Close</button>
</template>
</Modal>
<input
type="text"
placeholder="Start typing to filter"
class="input-bordered input input-sm my-1 ml-auto mr-1 max-w-xs flex-grow"
v-model="filter"
/>
</div>
<table class="table-compact table w-full"> <table class="table-compact table w-full">
<thead> <thead>
<tr> <tr>
<th> <th class="w-[48px]"></th>
<!-- <label>
<input
type="checkbox"
class="checkbox-primary checkbox checkbox-sm border-secondary"
/>
</label> -->
</th>
<th>Note title</th> <th>Note title</th>
<th>References</th> <th class="w-[100px]">References</th>
<th>Modified</th> <th class="w-[150px]">Modified</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr <tr
v-for="row in rows" v-for="note in notesWithReferences"
:key="row.noteTitle" :key="note.id"
class="hover hover:cursor-pointer" class="hover hover:cursor-pointer"
@click="toggleRow(row)" @click="setActiveNote(note.id)"
> >
<th> <th @click.stop="toggleRow(note)" class="text-center">
<label> <label>
<input <input
type="checkbox" type="checkbox"
class="checkbox-primary checkbox checkbox-sm border-secondary" class="checkbox-primary checkbox checkbox-sm border-secondary"
v-model="checkedRows[row.noteTitle]" :checked="Boolean(selectedNotes[note.id])"
:disabled="row.rootNote" :disabled="note.isRoot"
/> />
</label> </label>
</th> </th>
<td>{{ row.noteTitle }}</td> <td>
<td>{{ row.references }}</td> <i class="fas fa-fw fa-home mr-1 text-secondary" v-if="note.isRoot" />
<td>{{ row.modified }}</td> {{ note.title }}
</td>
<td>
<div class="badge" v-if="note.references.length > 0">
<i data-v-41bbc26f="" class="fas fa-fw fa-sign-out-alt mr-1"></i>
{{ note.references.length }}
</div>
</td>
<td>{{ formatDate(note.modified) }}</td>
</tr> </tr>
</tbody> </tbody>
</table> </table>

View File

@@ -1,13 +1,12 @@
<script setup lang="ts"> <script setup lang="ts">
import { formatDate } from '@/utils/helpers' import { formatDate } from '@/utils/helpers'
import { import {
notes,
activeNote, activeNote,
notesRelations,
deleteNote, deleteNote,
rootNote, rootNote,
setRootNote, setRootNote,
setActiveNote, setActiveNote,
getNoteReferences
} from '@/composables/useNotes' } from '@/composables/useNotes'
const props = defineProps<{ const props = defineProps<{
@@ -29,16 +28,7 @@ const updateNoteContent = (content: string) => {
emit('update', updatedNote) emit('update', updatedNote)
} }
const references = computed<Note[]>(() => { const references = computed<Note[]>(() => getNoteReferences(props.note))
const relations = notesRelations.value[props.note.id]
return relations
? (relations.from || [])
.map((noteId) => {
return notes.value.find((note) => note.id === noteId)
})
.filter((note): note is Note => note !== undefined)
: []
})
const del = async (closeModal: () => Promise<Boolean>) => { const del = async (closeModal: () => Promise<Boolean>) => {
await closeModal() await closeModal()
@@ -52,18 +42,14 @@ const setRoot = async (closeModal: () => Promise<Boolean>) => {
} }
</script> </script>
<template> <template>
<div class="flex flex-col h-full"> <div class="flex h-full flex-col">
<NoteToolbar :note="props.note" @delete="del" @set-root="setRoot"> <NoteToolbar :note="props.note" @delete="del" @set-root="setRoot">
<template #title> <template #title>
<i <i
class="fas fa-fw fa-home mr-2 text-base text-secondary opacity-40" class="fas fa-fw fa-home mr-2 text-base text-secondary opacity-40"
v-if="props.note.isRoot" v-if="props.note.isRoot"
></i> ></i>
<input <input type="text" class="flex-grow bg-transparent py-1 outline-none" v-model="noteTitle" />
type="text"
class="flex-grow bg-transparent py-1 outline-none"
v-model="noteTitle"
/>
</template> </template>
</NoteToolbar> </NoteToolbar>
<NoteEditor <NoteEditor

View File

@@ -10,18 +10,17 @@ export const notes = computed<Note[]>(() => {
return Object.entries(baseNotes) return Object.entries(baseNotes)
.map(([, note]) => ({ .map(([, note]) => ({
...note, ...note,
wordCount: note.content.split(' ').filter((word) => word.length > 0).length, wordCount: note.content.split(' ').filter((word) => word.length > 0).length
})) }))
.sort((a, b) => b.modified - a.modified) as Note[] .sort((a, b) => b.modified - a.modified) as Note[]
}) })
watch(notes, () => { watch(notes, () => {
if (notes.value.length > 0 && !activeNote.value) setActiveNote(rootNote.value?.id) if (notes.value.length > 0 && !activeNote.value && activeViewMode.value.name === 'Note')
setActiveNote(rootNote.value?.id)
}) })
const activeNoteId = ref<string>() const activeNoteId = ref<string>()
export const activeNote = computed(() => export const activeNote = computed(() => notes.value.find((note) => note.id === activeNoteId.value))
notes.value.find((note) => note.id === activeNoteId.value)
)
watch(activeNote, () => { watch(activeNote, () => {
if (activeNote.value) { if (activeNote.value) {
useTitle(`${activeNote.value.title} | Contexted`) useTitle(`${activeNote.value.title} | Contexted`)
@@ -30,8 +29,7 @@ watch(activeNote, () => {
export const setActiveNote = (noteId: string | undefined) => { export const setActiveNote = (noteId: string | undefined) => {
if (noteId) { if (noteId) {
activeNoteId.value = noteId activeNoteId.value = noteId
activeViewMode.value = activeViewMode.value = viewModes.find((mode) => mode.name === 'Note') || viewModes[0]
viewModes.find((mode) => mode.name === 'Note') || viewModes[0]
} }
} }
@@ -78,9 +76,7 @@ export const findNotes = (query: string): Note[] => {
} }
return notes.value.filter((note) => { return notes.value.filter((note) => {
const matchTitle = note.title.toLowerCase().includes(query.toLowerCase()) const matchTitle = note.title.toLowerCase().includes(query.toLowerCase())
const matchContent = removeMdFromText(note.content) const matchContent = removeMdFromText(note.content).toLowerCase().includes(query.toLowerCase())
.toLowerCase()
.includes(query.toLowerCase())
return matchTitle || matchContent return matchTitle || matchContent
}) })
} }
@@ -88,7 +84,7 @@ export const findNotes = (query: string): Note[] => {
export const updateNote = (noteId: string, note: BaseNote) => { export const updateNote = (noteId: string, note: BaseNote) => {
const updatedNote: BaseNote = { const updatedNote: BaseNote = {
...note, ...note,
modified: new Date().getTime(), modified: new Date().getTime()
} }
baseNotes[noteId] = updatedNote baseNotes[noteId] = updatedNote
} }
@@ -100,7 +96,7 @@ export const addNote = (title: string, content: string, goToNote: boolean = fals
title, title,
content, content,
created: new Date().getTime(), created: new Date().getTime(),
modified: new Date().getTime(), modified: new Date().getTime()
} }
baseNotes[id] = newNote baseNotes[id] = newNote
if (goToNote) setActiveNote(id) if (goToNote) setActiveNote(id)
@@ -143,16 +139,14 @@ export const notesRelations = computed(() => {
.map((noteRelations, _, notesRelations): NoteRelations => { .map((noteRelations, _, notesRelations): NoteRelations => {
const from = [...notesRelations] const from = [...notesRelations]
.map((noteRelation) => .map((noteRelation) =>
noteRelation.to noteRelation.to.filter((toId) => toId === noteRelations.id).map(() => noteRelation.id)
.filter((toId) => toId === noteRelations.id)
.map(() => noteRelation.id)
) )
.reduce((arr, elem) => arr.concat(elem), []) .reduce((arr, elem) => arr.concat(elem), [])
.filter((value, index, self) => self.indexOf(value) === index) .filter((value, index, self) => self.indexOf(value) === index)
return { return {
id: noteRelations.id, id: noteRelations.id,
to: noteRelations.to, to: noteRelations.to,
from, from
} }
}) })
.reduce((notes, { id, to, from }) => { .reduce((notes, { id, to, from }) => {
@@ -161,3 +155,14 @@ export const notesRelations = computed(() => {
}, {} as NotesRelations) }, {} as NotesRelations)
return relations return relations
}) })
export function getNoteReferences(note: Note) {
const relations = notesRelations.value[note.id]
return relations
? (relations.from || [])
.map((noteId) => {
return notes.value.find((note) => note.id === noteId)
})
.filter((note): note is Note => note !== undefined)
: []
}