Here is the first draft of a personnal project.
It's implemented using Mithril, but you can use whatever frontend library you want (uhtml,react,vue...jquery...).
The main point is that it uses the Meiosis pattern and BrowserFS for localStorage.
For the css part I use w3css.
Actual features :
- You can save a note
- You can display the notes list
- You can edit a note
more to come....
You can test it here BN
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://www.w3schools.com/w3css/4/w3.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/jodit/3.4.11/jodit.min.css" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/BrowserFS/2.0.0/browserfs.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jodit/3.4.11/jodit.min.js"></script>
<div class="w3-container">
<div id="app">
</div>
</div>
const merge = mergerino
const [div, span, ul, button, li] = ["div", "span", "ul", "button", "li"]
console.log(span)
const state = {
count: 0,
displayEditor: "block",
displayExplorer: "none",
contentEditor : "",
readedFileContent: "",
readedFileName: "",
sesamfiles : []
}
const update = m.stream();
const states = m.stream.scan(merge, state, update)
const actions = Actions(update)
// Setup FileSystem
function setupFS() {
BrowserFS.install(window);
BrowserFS.configure({
fs: "LocalStorage"
}, function(e) {
if (e) {
throw e;
}
fs = require('fs');
});
}
// Setup Editor
function setupEditor() {
let saveButton = {
text: "save",
tooltip: "Sauvegarder le contenu de l'éditeur",
iconURL: "https://uxwing.com/wp-content/themes/uxwing/download/03-text-editing/save.png",
exec: actions.saveEditor
}
let listNotesButton = {
text: "LIST",
tooltip: "Lister les notes",
exec: actions.showExplorer
}
let options = [
"bold", "|", "underline", "|", ,
"ul",
"align", "|",
"image", "|",
saveButton,
listNotesButton, "|",
"fullsize", "|"
]
editor = new Jodit("#editor", {
sizeLG: 900,
sizeMD: 700,
sizeSM: 400,
buttons: options,
buttonsMD: options,
buttonsSM: options,
buttonsXS: options,
enter: "div",
language: "fr",
uploader: {
insertImageAsBase64URI: true
},
imageDefaultWidth: 300
});
}
// Define all actions of the app
function Actions (update) {
const actions = {
showExplorer: function () {
actions.readDir()
update({
displayEditor: "none",
displayExplorer: "block",
})
},
showEditor: function () {
update({
displayEditor: "block",
displayExplorer: "none"
})
},
saveEditor: function () {
let content = editor.getEditorValue()
if (!content) return;
let filename = prompt("nom du fichier : ")
if (!filename) return;
filename = `sesam-${filename}`
update({
contentEditor: content
})
fs.writeFile(filename, content, function(err) {
fs.readFile(filename, function(err, contents) {
console.log(states().contentEditor);
});
});
},
readDir : function () {
fs.readdir('/', (e, files) => {
const sesamfiles = files.filter((f) => f.startsWith("sesam") )
const count = sesamfiles.length
update({ sesamfiles, count })
})
},
readFile: function (readedFileName) {
fs.readFile(readedFileName, function(err, content) {
const readedFileContent = bufferToString(content)
update({readedFileContent, readedFileName})
actions.showEditor()
editor.setEditorValue(states().readedFileContent)
});
}
}
return actions;
}
// Render a file item
function renderFileItem (filename) {
const vnode = m("li", {onclick: () => actions.readFile(filename)}, filename)
return vnode
}
function Explorer (state) {
return [
m(div, "Explorer " + `nbre de fichiers : ${state.count}`),
m(button,{onclick:actions.showEditor} ,"showeditor"),
m(div, state.sesamfiles.map(renderFileItem))
]
}
function view(state, actions) {
let style = (status) => ({style: `display:${status}`})
m.render(app, [
m(div, style(state.displayExplorer), Explorer(state)),
m(div, style(state.displayEditor), m("#editor"))
])
}
function main () {
setupFS()
states.map(state => view(state, actions))
setupEditor()
}
main()
/**** tools ***/
function bufferToString(buff) {
return String.fromCharCode.apply(null, new Uint16Array(buff))
}
Top comments (0)