diff --git a/frontend/editor.js b/frontend/editor.js new file mode 100644 index 0000000..7159865 --- /dev/null +++ b/frontend/editor.js @@ -0,0 +1,99 @@ + +import { EditorView, basicSetup } from "codemirror" +import { Compartment, Text } from "@codemirror/state" +import { markdown } from "@codemirror/lang-markdown" +import { marked } from "marked" + +import { RemoveDialog } from "./removedialog.js" +import { NotImplementedDialog } from "./notimplementeddialog.js" + +class Editor { + + #title + #tags + #active_note + #editor + #remove_dialog + + constructor() { + this.#active_note = null; + this.#title = document.querySelector("#editor-title"); + this.#tags = document.querySelector("#editor-tags"); + + document.querySelector("#editor-save").addEventListener("click", () => { + this.#save(); + }); + + this.#remove_dialog = new RemoveDialog(document.querySelector("#remove-dialog")); + document.querySelector("#editor-remove").addEventListener("click", () => { + this.#remove_dialog.show_modal(this.#active_note); + }); + + const notImplementedDialog = new NotImplementedDialog(); + document.querySelector("#editor-screenshot").addEventListener("click", () => { + notImplementedDialog.showModal(); + }); + document.querySelector("#editor-open-folder").addEventListener("click", () => { + notImplementedDialog.showModal(); + }); + + + const language = new Compartment(); + const editor_element = document.querySelector("#editor"); + this.#editor = new EditorView({ + extensions: [ + basicSetup, + language.of(markdown()) + ], + doc: "", + parent: editor_element + }); + + /* + editor.dom.addEventListener('input', async () => { + const text = editor.state.doc.toString(); + const html = marked.parse(text, { + pedantic: false, + gfm: true + }); + document.querySelector("#view").innerHTML = html; + }); + */ + + } + + async set_note(note) { + this.#save(); + this.#active_note = note; + this.#title.value = note.name; + this.#tags.value = note.tags.join(" "); + + this.#set_content(await note.get_content()); + } + + #save() { + if (this.#active_note) { + this.#active_note.save( + this.#title.value, + this.#editor.state.doc.toString(), + this.#tags.value.split(" ")); + } + } + + #set_content(content) { + this.#editor.dispatch({changes: [{ + from: 0, + to: this.#editor.state.doc.length, + insert: content + }]}); + } + + remove() { + this.#title.value = ""; + this.#tags.value = ""; + this.#set_content(""); + this.#active_note = null; + } +} + +export { Editor } diff --git a/frontend/fakenoteprovider.js b/frontend/fakenoteprovider.js index 9091b54..b485309 100644 --- a/frontend/fakenoteprovider.js +++ b/frontend/fakenoteprovider.js @@ -25,7 +25,9 @@ class FakeNoteProvider extends NoteProvider { #get_note(name) { const note = this.#notes.get(name); - if (!note) { throw new Error(`unknown note \"${name}\"`); } + if (!note) { + throw new Error(`unknown note \"${name}\"`); + } return note; } diff --git a/frontend/main.js b/frontend/main.js index b3057f7..74c0db6 100644 --- a/frontend/main.js +++ b/frontend/main.js @@ -1,46 +1,26 @@ import "lineicons/web-font/lineicons.css" -import { EditorView, basicSetup } from "codemirror" -import { Compartment, Text } from "@codemirror/state" -import { markdown } from "@codemirror/lang-markdown" -import { marked } from "marked" import { slider_attach } from "./slider.js" import { init_titlebar } from "./titlebar.js" import { init_settings } from "./settings.js" import { FakeNoteProvider } from "./fakenoteprovider.js" import { NoteList } from "./notelist.js" +import { Editor } from "./editor.js" init_titlebar(); init_settings(); slider_attach(document.querySelector("#slider")); +const editor = new Editor(); + const noteProvider = new FakeNoteProvider(); const notelist_element = document.querySelector("#notelist"); -const notelist = new NoteList(noteProvider, notelist_element); +const notelist = new NoteList(noteProvider, notelist_element, editor); document.querySelector("#add-note").addEventListener("click", async () => { notelist.add_new(); }) -const language = new Compartment(); -const editor_element = document.querySelector("#editor"); -const editor = new EditorView({ - extensions: [ - basicSetup, - language.of(markdown()) - ], - doc: "", - parent: editor_element -}); - -/* -editor.dom.addEventListener('input', async () => { - const text = editor.state.doc.toString(); - const html = marked.parse(text, { - pedantic: false, - gfm: true - }); - document.querySelector("#view").innerHTML = html; -}); -*/ + + diff --git a/frontend/note.js b/frontend/note.js index ad61f64..add7931 100644 --- a/frontend/note.js +++ b/frontend/note.js @@ -1,10 +1,18 @@ class Note { #name; + #tags; + #provider; + #editor; #list_item; + #notelist - constructor(name, notelist) { + constructor(name, tags, provider, notelist, editor) { this.#name = name; - this.#create_listentry(notelist); + this.#tags = tags; + this.#provider = provider; + this.#editor = editor; + this.#notelist = notelist; + this.#create_listentry(); } @@ -12,15 +20,53 @@ class Note { return this.#name; } - #create_listentry(notelist) { + get tags() { + return this.#tags; + } + + async get_content() { + return await this.#provider.read(this.#name); + } + + #create_listentry() { this.#list_item = document.createElement("li"); - notelist.element.appendChild(this.#list_item); + this.#notelist.element.appendChild(this.#list_item); this.#list_item.textContent = this.#name; this.#list_item.addEventListener('click', async () => { - console.log("ToDo: activate"); + this.#notelist.activate(this); }, false); } + + activate() { + this.#list_item.classList.add("active"); + this.#editor.set_note(this); + + } + + async deactivate() { + this.#list_item.classList.remove("active"); + } + + async save(name, content, tags) { + if (name != this.name) { + await this.#provider.rename(this.#name, name); + this.#list_item.textContent = name; + this.#notelist.rename(this.#name, name); + this.#name = name; + } + this.#provider.write(this.#name, content); + + this.#tags = tags; + this.#provider.write_tags(this.#name, tags); + } + + async remove() { + await this.#provider.remove(this.#name); + this.#list_item.remove(); + this.#editor.remove(); + this.#notelist.remove(this); + } } export { Note } diff --git a/frontend/notelist.js b/frontend/notelist.js index 21f6c11..8b3440e 100644 --- a/frontend/notelist.js +++ b/frontend/notelist.js @@ -4,11 +4,15 @@ class NoteList { #provider #element; + #editor; #notes; + #active_note; - constructor(provider, element) { + constructor(provider, element, editor) { this.#provider = provider; this.#element = element; + this.#editor = editor; + this.#active_note = null; this.#update(); } @@ -23,19 +27,48 @@ class NoteList { } } - async #add(name) { - const text = await this.#provider.read(name); + async #add(name, activate) { const tags = await this.#provider.read_tags(name); - const note = new Note(name, this); + const note = new Note(name, tags, this.#provider, this, this.#editor); + this.#notes.set(name, note); + if ((!this.#active_note) || (activate)) { + this.activate(note); + } } get element() { return this.#element; } + activate(note) { + if (this.#active_note) { + this.#active_note.deactivate(); + } + this.#active_note = note; + this.#active_note.activate(); + } + async add_new() { const name = await this.#provider.create(); - this.#add(name); + this.#add(name, true); + } + + rename(old_name, new_name) { + if (this.#notes.has(old_name)) { + const note = this.#notes.get(old_name); + this.#notes.delete(old_name); + this.#notes.set(new_name, note); + } + } + + remove(note) { + this.#notes.delete(note.name); + if (this.#active_note == note) { + this.#active_note = null; + if (this.#notes.size > 0) { + this.activate(this.#notes.values().next().value); + } + } } } diff --git a/frontend/notimplementeddialog.js b/frontend/notimplementeddialog.js new file mode 100644 index 0000000..2f9742d --- /dev/null +++ b/frontend/notimplementeddialog.js @@ -0,0 +1,17 @@ +class NotImplementedDialog { + + #dialog + + constructor() { + this.#dialog = document.querySelector("#not-implemented-dialog"); + this.#dialog.querySelector("[data-button=ok]").addEventListener("click", () => { + this.#dialog.close(); + }); + } + + showModal() { + this.#dialog.showModal(); + } +} + +export { NotImplementedDialog } \ No newline at end of file diff --git a/frontend/removedialog.js b/frontend/removedialog.js new file mode 100644 index 0000000..0cff664 --- /dev/null +++ b/frontend/removedialog.js @@ -0,0 +1,35 @@ + +class RemoveDialog { + + #dialog + #text + #note + + constructor(dialog) { + this.#dialog = dialog; + this.#text = dialog.querySelector("[data-text]"); + this.#note = null; + + dialog.querySelector("[data-button=yes]").addEventListener("click", async () => { + if (this.#note) { + this.#note.remove(); + } + this.#dialog.close() + }) + + dialog.querySelector("[data-button=no]").addEventListener("click", () => { + this.#dialog.close() + }) + } + + show_modal(note) { + if (note) { + this.#note = note; + this.#text.textContent = `Do you really want to remove note \"${note.name}\"?`; + this.#dialog.showModal(); + this.#dialog.querySelector("[data-button=no]").focus(); + } + } +} + +export { RemoveDialog } \ No newline at end of file diff --git a/frontend/style.css b/frontend/style.css index e4e0854..d6df007 100644 --- a/frontend/style.css +++ b/frontend/style.css @@ -60,7 +60,7 @@ body { } -.controls { +#sidebar .controls { display: flex; flex-direction: row; flex-wrap: nowrap; @@ -141,4 +141,10 @@ body { .toolbar .lni { line-height: inherit; +} + +dialog .controls { + display: flex; + flex-direction: row; + justify-content: flex-end; } \ No newline at end of file diff --git a/frontend/titlebar.js b/frontend/titlebar.js index 721dc03..3aef202 100644 --- a/frontend/titlebar.js +++ b/frontend/titlebar.js @@ -18,7 +18,6 @@ const init_titlebar = function() { const nav_main = document.querySelectorAll(".nav-main"); nav_main.forEach((item) => { - console.log("click"); item.addEventListener('click', () => { document.querySelector("#main").classList.remove("hidden"); document.querySelector("#settings").classList.add("hidden"); diff --git a/index.html b/index.html index 390704b..02bdd64 100644 --- a/index.html +++ b/index.html @@ -43,16 +43,16 @@
- -
-
-
+ +
+
+
-
+
- +

@@ -68,6 +68,21 @@

Color Settings

+ +

+
+ + +
+
+ + +

Not implemented yet.

+
+ +
+
+