DEV Community

artydev
artydev

Posted on

Implementing a BlocNote with Jodit

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>

Enter fullscreen mode Exit fullscreen mode
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)) 
}
Enter fullscreen mode Exit fullscreen mode

Top comments (0)