replace textarea with a content-editable div
Mainly because of the sluggish auto-resize "feature" which comes for free when using a content-editable div. If you use a custom CSS, make sure you replace textarea (element with .textarea (class) and set `white-space: pre`.
This commit is contained in:
parent
638ddc6359
commit
09451ff707
@ -27,6 +27,16 @@ a {
|
|||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
font-family: "Helvetica", Arial, sans-serif;
|
font-family: "Helvetica", Arial, sans-serif;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.textarea {
|
||||||
|
white-space: pre;
|
||||||
|
outline: 0px solid transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.textarea.placeholder {
|
||||||
|
color: #AAA;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.parent-highlight {
|
.parent-highlight {
|
||||||
@ -82,6 +92,10 @@ a {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
> .textarea-wrapper .textarea {
|
||||||
|
margin-top: 0.2em;
|
||||||
|
}
|
||||||
|
|
||||||
> div.text {
|
> div.text {
|
||||||
|
|
||||||
p {
|
p {
|
||||||
@ -98,9 +112,10 @@ a {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
> div.textarea-wrapper textarea {
|
> div.textarea-wrapper .textarea {
|
||||||
@include fill-parent;
|
@include fill-parent;
|
||||||
@include isso-shadow;
|
@include isso-shadow;
|
||||||
|
min-height: 48px;
|
||||||
font: inherit;
|
font: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -155,12 +170,9 @@ a {
|
|||||||
> .form-wrapper {
|
> .form-wrapper {
|
||||||
@include span-columns(10 of 11);
|
@include span-columns(10 of 11);
|
||||||
|
|
||||||
textarea {
|
.textarea {
|
||||||
@include fill-parent;
|
@include fill-parent;
|
||||||
@include isso-shadow;
|
@include isso-shadow;
|
||||||
@include placeholder {
|
|
||||||
color: #AAA;
|
|
||||||
}
|
|
||||||
|
|
||||||
min-height: 48px;
|
min-height: 48px;
|
||||||
font: inherit;
|
font: inherit;
|
||||||
|
@ -15,7 +15,7 @@ define(["app/text/html", "app/dom", "app/utils", "app/config", "app/api", "app/m
|
|||||||
$(".avatar > svg", el).replace(lib.identicons.blank(4, 48));
|
$(".avatar > svg", el).replace(lib.identicons.blank(4, 48));
|
||||||
|
|
||||||
// on text area focus, generate identicon from IP address
|
// on text area focus, generate identicon from IP address
|
||||||
$(".textarea-wrapper > textarea", el).on("focus", function() {
|
$(".textarea-wrapper > .textarea", el).on("focus", function() {
|
||||||
if ($(".avatar svg", el).getAttribute("className") === "blank") {
|
if ($(".avatar svg", el).getAttribute("className") === "blank") {
|
||||||
$(".avatar svg", el).replace(
|
$(".avatar svg", el).replace(
|
||||||
lib.identicons.generate(lib.pbkdf2(api.remote_addr(), api.salt, 1000, 6), 4, 48));
|
lib.identicons.generate(lib.pbkdf2(api.remote_addr(), api.salt, 1000, 6), 4, 48));
|
||||||
@ -45,8 +45,10 @@ define(["app/text/html", "app/dom", "app/utils", "app/config", "app/api", "app/m
|
|||||||
el.onsuccess = function() {};
|
el.onsuccess = function() {};
|
||||||
|
|
||||||
el.validate = function() {
|
el.validate = function() {
|
||||||
if ($("textarea", this).value.length < 3) {
|
if (utils.text($(".textarea", this).innerHTML).length < 3 ||
|
||||||
$("textarea", this).focus();
|
$(".textarea", this).classList.contains("placeholder"))
|
||||||
|
{
|
||||||
|
$(".textarea", this).focus();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@ -62,14 +64,13 @@ define(["app/text/html", "app/dom", "app/utils", "app/config", "app/api", "app/m
|
|||||||
api.create($("#isso-thread").getAttribute("data-isso-id"), {
|
api.create($("#isso-thread").getAttribute("data-isso-id"), {
|
||||||
author: $("[name=author]", el).value || null,
|
author: $("[name=author]", el).value || null,
|
||||||
email: $("[name=email]", el).value || null,
|
email: $("[name=email]", el).value || null,
|
||||||
text: $("textarea", el).value,
|
text: utils.text($(".textarea", el).innerHTML),
|
||||||
parent: parent || null
|
parent: parent || null
|
||||||
}).then(function(comment) {
|
}).then(function(comment) {
|
||||||
$("[name=author]", el).value = "";
|
$("[name=author]", el).value = "";
|
||||||
$("[name=email]", el).value = "";
|
$("[name=email]", el).value = "";
|
||||||
$("textarea", el).value = "";
|
$(".textarea", el).innerHTML = "";
|
||||||
$("textarea", el).rows = 2;
|
$(".textarea", el).blur();
|
||||||
$("textarea", el).blur();
|
|
||||||
insert(comment, true);
|
insert(comment, true);
|
||||||
|
|
||||||
if (parent !== null) {
|
if (parent !== null) {
|
||||||
@ -79,8 +80,7 @@ define(["app/text/html", "app/dom", "app/utils", "app/config", "app/api", "app/m
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// copy'n'paste sluggy automagically dynamic textarea resize
|
lib.editorify($(".textarea", el));
|
||||||
lib.fancy.autoresize($("textarea", el), 48);
|
|
||||||
|
|
||||||
return el;
|
return el;
|
||||||
};
|
};
|
||||||
@ -136,7 +136,7 @@ define(["app/text/html", "app/dom", "app/utils", "app/config", "app/api", "app/m
|
|||||||
function(toggler) {
|
function(toggler) {
|
||||||
form = footer.insertAfter(new Postbox(comment.id));
|
form = footer.insertAfter(new Postbox(comment.id));
|
||||||
form.onsuccess = function() { toggler.next(); };
|
form.onsuccess = function() { toggler.next(); };
|
||||||
$("textarea", form).focus();
|
$(".textarea", form).focus();
|
||||||
$("a.reply", footer).textContent = msgs["comment-close"];
|
$("a.reply", footer).textContent = msgs["comment-close"];
|
||||||
},
|
},
|
||||||
function() {
|
function() {
|
||||||
@ -186,34 +186,44 @@ define(["app/text/html", "app/dom", "app/utils", "app/config", "app/api", "app/m
|
|||||||
|
|
||||||
edit.textContent = msgs["comment-save"];
|
edit.textContent = msgs["comment-save"];
|
||||||
edit.insertAfter($.new("a.cancel", msgs["comment-cancel"])).on("click", function() {
|
edit.insertAfter($.new("a.cancel", msgs["comment-cancel"])).on("click", function() {
|
||||||
text.textContent = "";
|
toggler.canceled = true;
|
||||||
text.className = "text";
|
|
||||||
text.append(comment.text);
|
|
||||||
toggler.next();
|
toggler.next();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
toggler.canceled = false;
|
||||||
api.view(comment.id, 1).then(function(rv) {
|
api.view(comment.id, 1).then(function(rv) {
|
||||||
var textarea = $.new("textarea", rv.text);
|
var textarea = lib.editorify($.new("div.textarea"));
|
||||||
lib.fancy.autoresize(textarea, 48);
|
|
||||||
text.className = "textarea-wrapper";
|
textarea.textContent = rv.text;
|
||||||
|
textarea.focus();
|
||||||
|
|
||||||
|
text.classList.remove("text");
|
||||||
|
text.classList.add("textarea-wrapper");
|
||||||
|
|
||||||
text.textContent = "";
|
text.textContent = "";
|
||||||
text.append(textarea);
|
text.append(textarea);
|
||||||
textarea.focus();
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
function(toggler) {
|
function(toggler) {
|
||||||
var textarea = $("textarea", text);
|
var textarea = $(".textarea", text);
|
||||||
if (textarea && textarea.value.length < 3) {
|
|
||||||
|
if (! toggler.canceled && textarea !== null) {
|
||||||
|
if (utils.text(textarea.innerHTML).length < 3) {
|
||||||
textarea.focus();
|
textarea.focus();
|
||||||
toggler.wait();
|
toggler.wait();
|
||||||
return;
|
return;
|
||||||
} else if (textarea) {
|
} else {
|
||||||
api.modify(comment.id, {"text": textarea.value}).then(function(rv) {
|
api.modify(comment.id, {"text": utils.text(textarea.innerHTML)}).then(function(rv) {
|
||||||
text.innerHTML = rv.text;
|
text.innerHTML = rv.text;
|
||||||
text.className = "text";
|
|
||||||
comment.text = rv.text;
|
comment.text = rv.text;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
text.innerHTML = comment.text;
|
||||||
|
}
|
||||||
|
|
||||||
|
text.classList.remove("textarea-wrapper");
|
||||||
|
text.classList.add("text");
|
||||||
|
|
||||||
$("a.cancel", footer).remove();
|
$("a.cancel", footer).remove();
|
||||||
$("a.edit", footer).textContent = msgs["comment-edit"];
|
$("a.edit", footer).textContent = msgs["comment-edit"];
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
define(function (require) {
|
define(function (require) {
|
||||||
return {
|
return {
|
||||||
fancy: require("app/lib/fancy"),
|
editorify: require("app/lib/editor"),
|
||||||
identicons: require("app/lib/identicons"),
|
identicons: require("app/lib/identicons"),
|
||||||
pbkdf2: require("app/lib/pbkdf2"),
|
pbkdf2: require("app/lib/pbkdf2"),
|
||||||
sha1: require("app/lib/sha1")
|
sha1: require("app/lib/sha1")
|
||||||
|
24
isso/js/app/lib/editor.js
Normal file
24
isso/js/app/lib/editor.js
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
define(["app/dom", "app/markup"], function($, Mark) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
return function(el) {
|
||||||
|
el.setAttribute("contentEditable", true);
|
||||||
|
|
||||||
|
el.on("focus", function() {
|
||||||
|
if (el.classList.contains("placeholder")) {
|
||||||
|
el.innerHTML = "";
|
||||||
|
el.classList.remove("placeholder");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
el.on("blur", function() {
|
||||||
|
if (el.textContent.length === 0) {
|
||||||
|
el.textContent = Mark.up("{{ i18n-postbox-text }}");
|
||||||
|
el.classList.add("placeholder");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return el;
|
||||||
|
};
|
||||||
|
|
||||||
|
});
|
@ -1,20 +0,0 @@
|
|||||||
define(function() {
|
|
||||||
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
var autoresize = function(textarea, minheight) {
|
|
||||||
var offset= !window.opera ? (textarea.offsetHeight - textarea.clientHeight) : (textarea.offsetHeight + parseInt(window.getComputedStyle(textarea, null).getPropertyValue('border-top-width')));
|
|
||||||
["keyup", "focus"].forEach(function(event) {
|
|
||||||
textarea.on(event, function() {
|
|
||||||
if ((textarea.scrollHeight + offset ) > minheight) {
|
|
||||||
textarea.style.height = "auto";
|
|
||||||
textarea.style.height = (textarea.scrollHeight + offset ) + 'px';
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
|
||||||
autoresize: autoresize
|
|
||||||
};
|
|
||||||
});
|
|
@ -4,7 +4,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="form-wrapper">
|
<div class="form-wrapper">
|
||||||
<div class="textarea-wrapper">
|
<div class="textarea-wrapper">
|
||||||
<textarea name="text" rows="2" placeholder="{{ i18n-postbox-text }}"></textarea>
|
<div class="textarea placeholder" contenteditable="true">{{ i18n-postbox-text }}</div>
|
||||||
</div>
|
</div>
|
||||||
<section class="auth-section">
|
<section class="auth-section">
|
||||||
<p class="input-wrapper">
|
<p class="input-wrapper">
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
define(["app/markup"], function(Mark) {
|
define(["app/markup"], function(Mark) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
// return `cookie` string if set
|
// return `cookie` string if set
|
||||||
var cookie = function(cookie) {
|
var cookie = function(cookie) {
|
||||||
@ -7,7 +8,7 @@ define(["app/markup"], function(Mark) {
|
|||||||
|
|
||||||
var ago = function(localTime, date) {
|
var ago = function(localTime, date) {
|
||||||
|
|
||||||
var secs = ((localTime.getTime() - date.getTime()) / 1000)
|
var secs = ((localTime.getTime() - date.getTime()) / 1000);
|
||||||
|
|
||||||
if (isNaN(secs) || secs < 0 ) {
|
if (isNaN(secs) || secs < 0 ) {
|
||||||
secs = 0;
|
secs = 0;
|
||||||
@ -39,8 +40,17 @@ define(["app/markup"], function(Mark) {
|
|||||||
i18n("date-year", Math.ceil(days / 365.25));
|
i18n("date-year", Math.ceil(days / 365.25));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var text = function(html) {
|
||||||
|
var _ = document.createElement("div");
|
||||||
|
_.innerHTML = html.replace(/<div><br><\/div>/gi, '<br>')
|
||||||
|
.replace(/<div>/gi,'<br>')
|
||||||
|
.replace(/<br>/gi, '\n');
|
||||||
|
return _.textContent.trim();
|
||||||
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
cookie: cookie,
|
cookie: cookie,
|
||||||
ago: ago
|
ago: ago,
|
||||||
|
text: text
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user