New editor (#458)

With this PR, when JS is activated and WASM supported, the article editor will be dynamically replaced with `contenteditable`s elements. This makes the editing interface simpler and less like a regular form. It will also allow us to easily add visual formatting with native browser APIs (and to insert images or videos directly). Here is a little demo:

![peek 05-03-2019 16-12](https://user-images.githubusercontent.com/16254623/53815536-1dc05680-3f62-11e9-94d3-b363ed84eb97.gif)

There is still a lot to do, but it is a good first step.

Fixes #255
This commit is contained in:
Baptiste Gelez
2019-03-15 16:06:10 +01:00
committed by GitHub
parent ffcbc0fde3
commit 9076dbaadc
25 changed files with 618 additions and 156 deletions
+26 -46
View File
@@ -1,56 +1,36 @@
#![recursion_limit="128"]
#![feature(decl_macro, proc_macro_hygiene, try_trait)]
extern crate gettext;
#[macro_use]
extern crate gettext_macros;
#[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate stdweb;
use stdweb::{unstable::{TryFrom, TryInto}, web::{*, event::*}};
use stdweb::{web::{*, event::*}};
fn main() {
editor_loop();
menu();
search();
init_i18n!("plume-front", en, fr);
mod editor;
compile_i18n!();
lazy_static! {
static ref CATALOG: gettext::Catalog = {
let catalogs = include_i18n!();
let lang = js!{ return navigator.language }.into_string().unwrap();
let lang = lang.splitn(2, '-').next().unwrap_or("en");
catalogs.iter().find(|(l, _)| l == &lang).unwrap_or(&catalogs[0]).clone().1
};
}
/// Auto expands the editor when adding text and count chars
fn editor_loop() {
match document().query_selector("#plume-editor") {
Ok(Some(x)) => HtmlElement::try_from(x).map(|article_content| {
let offset = article_content.offset_height() - (article_content.get_bounding_client_rect().get_height() as i32);
article_content.add_event_listener(move |_: KeyDownEvent| {
let article_content = document().query_selector("#plume-editor").ok();
js! {
@{&article_content}.style.height = "auto";
@{&article_content}.style.height = @{&article_content}.scrollHeight - @{offset} + "px";
};
window().set_timeout(|| {match document().query_selector("#post-form") {
Ok(Some(form)) => HtmlElement::try_from(form).map(|form| {
if let Some(len) = form.get_attribute("content-size").and_then(|s| s.parse::<i32>().ok()) {
let consumed: i32 = js!{
var len = - 1;
for(var i = 0; i < @{&form}.length; i++) {
if(@{&form}[i].name != "") {
len += @{&form}[i].name.length + encodeURIComponent(@{&form}[i].value)
.replace(/%20/g, "+")
.replace(/%0A/g, "%0D%0A")
.replace(new RegExp("[!'*()]", "g"), "XXX") //replace exceptions of encodeURIComponent with placeholder
.length + 2;
}
}
return len;
}.try_into().unwrap_or_default();
match document().query_selector("#editor-left") {
Ok(Some(e)) => HtmlElement::try_from(e).map(|e| {
js!{@{e}.innerText = (@{len-consumed})};
}).ok(),
_ => None,
};
}
}).ok(),
_ => None,
};}, 0);
});
}).ok(),
_ => None
};
fn main() {
menu();
search();
editor::init()
.map_err(|e| console!(error, format!("Editor error: {:?}", e))).ok();
}
/// Toggle menu on mobile device