emoji button and a lot of bug fixes
This commit is contained in:
parent
a33755f6ae
commit
fbd7c38f44
6 changed files with 182 additions and 45 deletions
|
@ -134,8 +134,16 @@ class Emoji {
|
|||
});
|
||||
const menu = document.createElement("div");
|
||||
menu.classList.add("flexttb", "emojiPicker");
|
||||
menu.style.top = y + "px";
|
||||
menu.style.left = x + "px";
|
||||
if (y > 0) {
|
||||
menu.style.top = y + "px";
|
||||
} else {
|
||||
menu.style.bottom = y * -1 + "px";
|
||||
}
|
||||
if (x > 0) {
|
||||
menu.style.left = x + "px";
|
||||
} else {
|
||||
menu.style.right = x * -1 + "px";
|
||||
}
|
||||
|
||||
const topBar = document.createElement("div");
|
||||
topBar.classList.add("flexltr", "emojiHeading");
|
||||
|
|
|
@ -85,10 +85,10 @@
|
|||
<div id="channelw" class="flexltr">
|
||||
<div id="loadingdiv"></div>
|
||||
</div>
|
||||
<div id="ghostMessages"></div>
|
||||
<div style="position: relative">
|
||||
<div id="searchOptions" class="flexttb searchOptions"></div>
|
||||
</div>
|
||||
<div id="ghostMessages"></div>
|
||||
<div id="pasteimage" class="flexltr"></div>
|
||||
<div id="replybox" class="hideReplyBox"></div>
|
||||
<div id="typediv">
|
||||
|
@ -96,6 +96,7 @@
|
|||
<div class="outerTypeBox">
|
||||
<span class="svg-upload svgicon" id="upload"></span>
|
||||
<div id="typebox" contenteditable="true"></div>
|
||||
<span class="svgicon svg-emoji" id="emojiTB"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div id="typing" class="hidden flexltr">
|
||||
|
|
|
@ -267,4 +267,11 @@ import {I18n} from "./i18n.js";
|
|||
}
|
||||
};
|
||||
};
|
||||
const emojiTB = document.getElementById("emojiTB") as HTMLElement;
|
||||
emojiTB.onmousedown = (e) => {
|
||||
e.preventDefault();
|
||||
e.stopImmediatePropagation();
|
||||
thisUser.TBEmojiMenu(emojiTB.getBoundingClientRect());
|
||||
};
|
||||
emojiTB.onclick = (e) => e.stopImmediatePropagation();
|
||||
})();
|
||||
|
|
|
@ -20,7 +20,7 @@ import {
|
|||
} from "./jsontypes.js";
|
||||
import {Member} from "./member.js";
|
||||
import {Dialog, Form, FormError, Options, Settings} from "./settings.js";
|
||||
import {getTextNodeAtPosition, MarkDown} from "./markdown.js";
|
||||
import {getTextNodeAtPosition, MarkDown, saveCaretPosition} from "./markdown.js";
|
||||
import {Bot} from "./bot.js";
|
||||
import {Role} from "./role.js";
|
||||
import {VoiceFactory} from "./voice.js";
|
||||
|
@ -33,7 +33,9 @@ import {Rights} from "./rights.js";
|
|||
import {Contextmenu} from "./contextmenu.js";
|
||||
|
||||
const wsCodesRetry = new Set([4000, 4001, 4002, 4003, 4005, 4007, 4008, 4009]);
|
||||
|
||||
interface CustomHTMLDivElement extends HTMLDivElement {
|
||||
markdown: MarkDown;
|
||||
}
|
||||
class Localuser {
|
||||
badges = new Map<
|
||||
string,
|
||||
|
@ -2229,10 +2231,6 @@ class Localuser {
|
|||
}
|
||||
readonly autofillregex = Object.freeze(/[@#:]([a-z0-9 ]*)$/i);
|
||||
mdBox() {
|
||||
interface CustomHTMLDivElement extends HTMLDivElement {
|
||||
markdown: MarkDown;
|
||||
}
|
||||
|
||||
const typebox = document.getElementById("typebox") as CustomHTMLDivElement;
|
||||
const typeMd = typebox.markdown;
|
||||
typeMd.owner = this;
|
||||
|
@ -2240,18 +2238,57 @@ class Localuser {
|
|||
this.search(document.getElementById("searchOptions") as HTMLDivElement, typeMd, str, pre);
|
||||
};
|
||||
}
|
||||
MDReplace(replacewith: string, original: string, typebox: MarkDown) {
|
||||
async TBEmojiMenu(rect: DOMRect) {
|
||||
const typebox = document.getElementById("typebox") as CustomHTMLDivElement;
|
||||
const p = saveCaretPosition(typebox);
|
||||
if (!p) return;
|
||||
const original = MarkDown.getText();
|
||||
console.log(original);
|
||||
|
||||
const emoji = await Emoji.emojiPicker(
|
||||
-10 + rect.left - window.screen.width,
|
||||
-5 + rect.top - window.screen.height,
|
||||
this,
|
||||
);
|
||||
p();
|
||||
const md = typebox.markdown;
|
||||
this.MDReplace(
|
||||
emoji.id
|
||||
? `<${emoji.animated ? "a" : ""}:${emoji.name}:${emoji.id}>`
|
||||
: (emoji.emoji as string),
|
||||
original,
|
||||
md,
|
||||
null,
|
||||
);
|
||||
//*/
|
||||
}
|
||||
MDReplace(
|
||||
replacewith: string,
|
||||
original: string,
|
||||
typebox: MarkDown,
|
||||
start: RegExp | null = this.autofillregex,
|
||||
) {
|
||||
let raw = typebox.rawString;
|
||||
raw = raw.split(original)[1];
|
||||
if (raw === undefined) return;
|
||||
raw = original.replace(this.autofillregex, "") + replacewith + raw;
|
||||
let empty = raw.length === 0;
|
||||
raw = original !== "" ? raw.split(original)[1] : raw;
|
||||
if (raw === undefined && !empty) return;
|
||||
if (empty) {
|
||||
raw = "";
|
||||
}
|
||||
raw = (start ? original.replace(start, "") : original) + replacewith + raw;
|
||||
console.log(raw);
|
||||
console.log(replacewith);
|
||||
console.log(original);
|
||||
typebox.txt = raw.split("");
|
||||
const match = original.match(this.autofillregex);
|
||||
console.log(typebox.rawString);
|
||||
const match = start ? original.match(start) : true;
|
||||
if (match) {
|
||||
typebox.boxupdate(replacewith.length - match[0].length);
|
||||
console.log(match);
|
||||
typebox.boxupdate(
|
||||
replacewith.length - (match === true ? 0 : match[0].length),
|
||||
false,
|
||||
original.length,
|
||||
);
|
||||
}
|
||||
}
|
||||
MDSearchOptions(
|
||||
|
@ -2339,8 +2376,7 @@ class Localuser {
|
|||
break;
|
||||
case "Enter":
|
||||
case "Tab":
|
||||
//@ts-ignore
|
||||
cur.onclick();
|
||||
cur.click();
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
|
@ -2437,6 +2473,7 @@ class Localuser {
|
|||
await Member.new(thing, this.lookingguild as Guild);
|
||||
}
|
||||
}
|
||||
if (!typebox.rawString.startsWith(original)) return;
|
||||
this.MDFineMentionGen(name, original, box, typebox);
|
||||
}
|
||||
});
|
||||
|
@ -2475,6 +2512,8 @@ class Localuser {
|
|||
this.MDSearchOptions([], "", box, md);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -46,6 +46,9 @@ class MarkDown {
|
|||
get textContent() {
|
||||
return this.makeHTML().textContent;
|
||||
}
|
||||
static getText() {
|
||||
return text;
|
||||
}
|
||||
makeHTML({keep = this.keep, stdsize = this.stdsize} = {}) {
|
||||
return this.markdown(this.txt, {keep, stdsize});
|
||||
}
|
||||
|
@ -67,6 +70,9 @@ class MarkDown {
|
|||
current = document.createElement("span");
|
||||
}
|
||||
}
|
||||
function getCurLast(): Element | undefined {
|
||||
return span.children[span.children.length - 1];
|
||||
}
|
||||
for (let i = 0; i < txt.length; i++) {
|
||||
if (txt[i] === "\n" || i === 0) {
|
||||
const first = i === 0;
|
||||
|
@ -143,6 +149,10 @@ class MarkDown {
|
|||
}
|
||||
if (txt[i] === "\n") {
|
||||
if (!stdsize) {
|
||||
const last = getCurLast();
|
||||
if (last instanceof HTMLElement && last.contentEditable === "false") {
|
||||
span.append(document.createElement("span"));
|
||||
}
|
||||
appendcurrent();
|
||||
span.append(document.createElement("br"));
|
||||
}
|
||||
|
@ -460,6 +470,13 @@ class MarkDown {
|
|||
|
||||
if (txt[j] === ">") {
|
||||
appendcurrent();
|
||||
const last = getCurLast();
|
||||
if (
|
||||
last instanceof HTMLBRElement ||
|
||||
(last instanceof HTMLElement && last.contentEditable === "false")
|
||||
) {
|
||||
span.append(document.createElement("span"));
|
||||
}
|
||||
const mention = document.createElement("span");
|
||||
mention.classList.add("mentionMD");
|
||||
mention.contentEditable = "false";
|
||||
|
@ -680,6 +697,15 @@ class MarkDown {
|
|||
current.textContent += txt[i];
|
||||
}
|
||||
appendcurrent();
|
||||
const last = getCurLast();
|
||||
if (
|
||||
last &&
|
||||
last instanceof HTMLElement &&
|
||||
last.contentEditable &&
|
||||
!(last instanceof HTMLBRElement)
|
||||
) {
|
||||
span.append(current);
|
||||
}
|
||||
return span;
|
||||
}
|
||||
static unspoil(e: any): void {
|
||||
|
@ -696,7 +722,8 @@ class MarkDown {
|
|||
};
|
||||
let prevcontent = "";
|
||||
box.onkeyup = (_) => {
|
||||
const content = MarkDown.gatherBoxText(box);
|
||||
let content = MarkDown.gatherBoxText(box);
|
||||
if (content === "\n") content = "";
|
||||
if (content !== prevcontent) {
|
||||
prevcontent = content;
|
||||
this.txt = content.split("");
|
||||
|
@ -725,14 +752,19 @@ class MarkDown {
|
|||
) {
|
||||
this.customBox = [stringToHTML, HTMLToString];
|
||||
}
|
||||
boxupdate(offset = 0) {
|
||||
boxupdate(offset = 0, allowLazy = true, computedLength: void | number = undefined) {
|
||||
const box = this.box.deref();
|
||||
if (!box) return;
|
||||
let restore: undefined | (() => void);
|
||||
if (this.customBox) {
|
||||
restore = saveCaretPosition(box, offset, this.customBox[1]);
|
||||
restore = saveCaretPosition(box, offset, this.customBox[1], computedLength);
|
||||
} else {
|
||||
restore = saveCaretPosition(box, offset);
|
||||
restore = saveCaretPosition(
|
||||
box,
|
||||
offset,
|
||||
MarkDown.gatherBoxText.bind(MarkDown),
|
||||
computedLength,
|
||||
);
|
||||
}
|
||||
|
||||
if (this.customBox) {
|
||||
|
@ -748,10 +780,13 @@ class MarkDown {
|
|||
html.childNodes[0].childNodes[0];
|
||||
//console.log(box.cloneNode(true), html.cloneNode(true));
|
||||
//TODO this may be slow, may want to check in on this in the future if it is
|
||||
if (!box.hasChildNodes() || html.isEqualNode(Array.from(box.childNodes)[0])) {
|
||||
if ((!box.hasChildNodes() || html.isEqualNode(Array.from(box.childNodes)[0])) && allowLazy) {
|
||||
//console.log("no replace needed");
|
||||
} else {
|
||||
if (!(box.childNodes.length === 1 && box.childNodes[0] instanceof Text && condition)) {
|
||||
if (
|
||||
!(box.childNodes.length === 1 && box.childNodes[0] instanceof Text && condition) ||
|
||||
!allowLazy
|
||||
) {
|
||||
box.innerHTML = "";
|
||||
box.append(html);
|
||||
} else {
|
||||
|
@ -762,13 +797,6 @@ class MarkDown {
|
|||
}
|
||||
if (restore) {
|
||||
restore();
|
||||
if (this.customBox) {
|
||||
const test = saveCaretPosition(box, 0, this.customBox[1]);
|
||||
if (test) test();
|
||||
} else {
|
||||
const test = saveCaretPosition(box);
|
||||
if (test) test();
|
||||
}
|
||||
}
|
||||
this.onUpdate(text, formatted);
|
||||
}
|
||||
|
@ -864,6 +892,7 @@ function saveCaretPosition(
|
|||
context: HTMLElement,
|
||||
offset = 0,
|
||||
txtLengthFunc = MarkDown.gatherBoxText.bind(MarkDown),
|
||||
computedLength: void | number = undefined,
|
||||
) {
|
||||
const selection = window.getSelection() as Selection;
|
||||
if (!selection) return;
|
||||
|
@ -873,26 +902,39 @@ function saveCaretPosition(
|
|||
let base = selection.anchorNode as Node;
|
||||
range.setStart(base, 0);
|
||||
let baseString: string;
|
||||
if (!(base instanceof Text)) {
|
||||
let i = 0;
|
||||
const index = selection.focusOffset;
|
||||
//@ts-ignore
|
||||
for (const thing of base.childNodes) {
|
||||
if (i === index) {
|
||||
base = thing;
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
let i = 0;
|
||||
const index = selection.focusOffset;
|
||||
|
||||
for (const thing of Array.from(base.childNodes)) {
|
||||
if (i === index) {
|
||||
base = thing;
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
console.log(base);
|
||||
const prev = base.previousSibling;
|
||||
let len = 0;
|
||||
if ((!prev || prev instanceof HTMLBRElement) && base instanceof HTMLBRElement) {
|
||||
len--;
|
||||
}
|
||||
if (
|
||||
!(base instanceof Text) &&
|
||||
!(
|
||||
base instanceof HTMLSpanElement &&
|
||||
base.className === "" &&
|
||||
base.children.length == 0 &&
|
||||
!(base instanceof HTMLBRElement)
|
||||
)
|
||||
) {
|
||||
if (base instanceof HTMLElement) {
|
||||
baseString = txtLengthFunc(base);
|
||||
} else {
|
||||
baseString = base.textContent as string;
|
||||
baseString = base.textContent || "";
|
||||
}
|
||||
} else {
|
||||
baseString = selection.toString();
|
||||
}
|
||||
|
||||
range.setStart(context, 0);
|
||||
|
||||
let build = "";
|
||||
|
@ -937,8 +979,14 @@ function saveCaretPosition(
|
|||
build += baseString;
|
||||
}
|
||||
text = build;
|
||||
let len = build.length + offset;
|
||||
len += build.length;
|
||||
if (computedLength !== undefined) {
|
||||
len = computedLength;
|
||||
}
|
||||
len = Math.min(len, txtLengthFunc(context).length);
|
||||
console.log(len, base);
|
||||
len += offset;
|
||||
console.log(len);
|
||||
return function restore() {
|
||||
if (!selection) return;
|
||||
const pos = getTextNodeAtPosition(context, len, txtLengthFunc);
|
||||
|
@ -960,6 +1008,12 @@ function getTextNodeAtPosition(
|
|||
node: Node;
|
||||
position: number;
|
||||
} {
|
||||
if (index === 0) {
|
||||
return {
|
||||
node: root,
|
||||
position: 0,
|
||||
};
|
||||
}
|
||||
if (root instanceof Text) {
|
||||
return {
|
||||
node: root,
|
||||
|
@ -976,6 +1030,7 @@ function getTextNodeAtPosition(
|
|||
position: -1,
|
||||
};
|
||||
}
|
||||
|
||||
let lastElm: Node = root;
|
||||
for (const node of root.childNodes as unknown as Node[]) {
|
||||
lastElm = node;
|
||||
|
@ -983,10 +1038,31 @@ function getTextNodeAtPosition(
|
|||
if (node instanceof HTMLElement) {
|
||||
len = txtLengthFunc(node).length;
|
||||
} else {
|
||||
len = (node.textContent as string).length;
|
||||
len = (node.textContent || "").length;
|
||||
}
|
||||
if (len <= index && (len < index || len !== 0)) {
|
||||
index -= len;
|
||||
if (index === 0) {
|
||||
let nodey = node;
|
||||
let bad = false;
|
||||
while (nodey.childNodes.length) {
|
||||
nodey = Array.from(nodey.childNodes).at(-1) as ChildNode;
|
||||
if (nodey instanceof HTMLElement && nodey.contentEditable === "false") {
|
||||
bad = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (
|
||||
!(nodey instanceof HTMLBRElement) &&
|
||||
(!(nodey instanceof HTMLElement) || nodey.contentEditable === "false") &&
|
||||
!bad
|
||||
) {
|
||||
return {
|
||||
node: nodey,
|
||||
position: (nodey.textContent || "").length,
|
||||
};
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const returny = getTextNodeAtPosition(node, index);
|
||||
if (returny.position === -1) {
|
||||
|
|
|
@ -432,6 +432,11 @@ textarea {
|
|||
mask-repeat: no-repeat;
|
||||
aspect-ratio: 1/1;
|
||||
}
|
||||
#emojiTB{
|
||||
width:.2in;
|
||||
height:.2in;
|
||||
cursor: pointer;
|
||||
}
|
||||
.selectarrow {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
|
@ -1143,6 +1148,7 @@ span.instanceStatus {
|
|||
flex-grow: 1;
|
||||
width: 100%;
|
||||
margin-left: 0.06in;
|
||||
white-space: pre;
|
||||
}
|
||||
.outerTypeBox {
|
||||
max-height: 50svh;
|
||||
|
@ -1499,7 +1505,7 @@ span .quote:last-of-type .quoteline {
|
|||
cursor: pointer;
|
||||
}
|
||||
.mentionMD::after {
|
||||
content: "";
|
||||
/* content: ""; */
|
||||
}
|
||||
.mentionMD:hover {
|
||||
background: var(--mention);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue