Support pasting styled text
This commit is contained in:
parent
135e8cdc34
commit
318aaf6f4e
@ -2918,7 +2918,10 @@ export default class ChatBubbles {
|
|||||||
const html = RichTextProcessor.wrapRichText(webpage.url);
|
const html = RichTextProcessor.wrapRichText(webpage.url);
|
||||||
const a: HTMLAnchorElement = htmlToDocumentFragment(html).firstElementChild as any;
|
const a: HTMLAnchorElement = htmlToDocumentFragment(html).firstElementChild as any;
|
||||||
a.classList.add('webpage-name');
|
a.classList.add('webpage-name');
|
||||||
|
// const b = document.createElement('b');
|
||||||
setInnerHTML(a, RichTextProcessor.wrapEmojiText(webpage.site_name));
|
setInnerHTML(a, RichTextProcessor.wrapEmojiText(webpage.site_name));
|
||||||
|
// a.textContent = '';
|
||||||
|
// a.append(b);
|
||||||
quoteTextDiv.append(a);
|
quoteTextDiv.append(a);
|
||||||
t = a;
|
t = a;
|
||||||
}
|
}
|
||||||
@ -2926,6 +2929,8 @@ export default class ChatBubbles {
|
|||||||
if(webpage.rTitle) {
|
if(webpage.rTitle) {
|
||||||
let titleDiv = document.createElement('div');
|
let titleDiv = document.createElement('div');
|
||||||
titleDiv.classList.add('title');
|
titleDiv.classList.add('title');
|
||||||
|
// const b = document.createElement('b');
|
||||||
|
// titleDiv.append(b);
|
||||||
setInnerHTML(titleDiv, webpage.rTitle);
|
setInnerHTML(titleDiv, webpage.rTitle);
|
||||||
quoteTextDiv.append(titleDiv);
|
quoteTextDiv.append(titleDiv);
|
||||||
t = titleDiv;
|
t = titleDiv;
|
||||||
|
@ -2405,6 +2405,10 @@ export default class ChatInput {
|
|||||||
replyParent.insertBefore(newReply, replyParent.lastElementChild);
|
replyParent.insertBefore(newReply, replyParent.lastElementChild);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(type === 'webpage') {
|
||||||
|
newReply.style.cursor = 'default';
|
||||||
|
}
|
||||||
|
|
||||||
if(!this.chat.container.classList.contains('is-helper-active')) {
|
if(!this.chat.container.classList.contains('is-helper-active')) {
|
||||||
this.chat.container.classList.add('is-helper-active');
|
this.chat.container.classList.add('is-helper-active');
|
||||||
this.t();
|
this.t();
|
||||||
|
@ -9,6 +9,7 @@ import findUpAttribute from "../helpers/dom/findUpAttribute";
|
|||||||
import getRichValue from "../helpers/dom/getRichValue";
|
import getRichValue from "../helpers/dom/getRichValue";
|
||||||
import isInputEmpty from "../helpers/dom/isInputEmpty";
|
import isInputEmpty from "../helpers/dom/isInputEmpty";
|
||||||
import selectElementContents from "../helpers/dom/selectElementContents";
|
import selectElementContents from "../helpers/dom/selectElementContents";
|
||||||
|
import { MessageEntity } from "../layer";
|
||||||
import { i18n, LangPackKey, _i18n } from "../lib/langPack";
|
import { i18n, LangPackKey, _i18n } from "../lib/langPack";
|
||||||
import RichTextProcessor from "../lib/richtextprocessor";
|
import RichTextProcessor from "../lib/richtextprocessor";
|
||||||
import SetTransition from "./singleTransition";
|
import SetTransition from "./singleTransition";
|
||||||
@ -18,27 +19,33 @@ let init = () => {
|
|||||||
if(!findUpAttribute(e.target, 'contenteditable="true"')) {
|
if(!findUpAttribute(e.target, 'contenteditable="true"')) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
//console.log('document paste');
|
|
||||||
|
|
||||||
//console.log('messageInput paste');
|
|
||||||
|
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
// @ts-ignore
|
let text: string, entities: MessageEntity[];
|
||||||
let text = (e.originalEvent || e).clipboardData.getData('text/plain');
|
|
||||||
|
|
||||||
let entities = RichTextProcessor.parseEntities(text);
|
|
||||||
//console.log('messageInput paste', text, entities);
|
|
||||||
entities = entities.filter(e => e._ === 'messageEntityEmoji' || e._ === 'messageEntityLinebreak');
|
|
||||||
//text = RichTextProcessor.wrapEmojiText(text);
|
|
||||||
text = RichTextProcessor.wrapRichText(text, {entities, noLinks: true, wrappingDraft: true});
|
|
||||||
|
|
||||||
// console.log('messageInput paste after', text);
|
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
//let html = (e.originalEvent || e).clipboardData.getData('text/html');
|
const html: string = (e.originalEvent || e).clipboardData.getData('text/html');
|
||||||
|
if(html.trim()) {
|
||||||
|
const span = document.createElement('span');
|
||||||
|
span.innerHTML = html;
|
||||||
|
|
||||||
|
const richValue = getRichValue(span, true);
|
||||||
|
text = richValue.value;
|
||||||
|
entities = richValue.entities;
|
||||||
|
|
||||||
// @ts-ignore
|
let entities2 = RichTextProcessor.parseEntities(text);
|
||||||
//console.log('paste text', text, );
|
entities2 = entities2.filter(e => e._ === 'messageEntityEmoji' || e._ === 'messageEntityLinebreak');
|
||||||
|
RichTextProcessor.mergeEntities(entities, entities2);
|
||||||
|
} else {
|
||||||
|
// @ts-ignore
|
||||||
|
text = (e.originalEvent || e).clipboardData.getData('text/plain');
|
||||||
|
|
||||||
|
entities = RichTextProcessor.parseEntities(text);
|
||||||
|
entities = entities.filter(e => e._ === 'messageEntityEmoji' || e._ === 'messageEntityLinebreak');
|
||||||
|
}
|
||||||
|
|
||||||
|
text = RichTextProcessor.wrapDraftText(text, {entities});
|
||||||
|
|
||||||
window.document.execCommand('insertHTML', false, text);
|
window.document.execCommand('insertHTML', false, text);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ export type MarkdownTag = {
|
|||||||
};
|
};
|
||||||
export const markdownTags: {[type in MarkdownType]: MarkdownTag} = {
|
export const markdownTags: {[type in MarkdownType]: MarkdownTag} = {
|
||||||
bold: {
|
bold: {
|
||||||
match: '[style*="font-weight"], b',
|
match: '[style*="bold"], [style*="font-weight: 700"], [style*="font-weight: 600"], [style*="font-weight:700"], [style*="font-weight:600"], b, strong',
|
||||||
entityName: 'messageEntityBold'
|
entityName: 'messageEntityBold'
|
||||||
},
|
},
|
||||||
underline: {
|
underline: {
|
||||||
@ -47,9 +47,46 @@ export const markdownTags: {[type in MarkdownType]: MarkdownTag} = {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const tabulationMatch = '[style*="table-cell"]';
|
||||||
|
|
||||||
|
/* export function getDepth(child: Node, container?: Node) {
|
||||||
|
let depth = 0;
|
||||||
|
|
||||||
|
do {
|
||||||
|
if(child === container) {
|
||||||
|
return depth;
|
||||||
|
}
|
||||||
|
|
||||||
|
++depth;
|
||||||
|
} while((child = child.parentNode) !== null);
|
||||||
|
|
||||||
|
return depth;
|
||||||
|
} */
|
||||||
|
|
||||||
|
const BLOCK_TAG_NAMES = new Set([
|
||||||
|
'DIV',
|
||||||
|
'P',
|
||||||
|
'BR',
|
||||||
|
'LI',
|
||||||
|
'SECTION',
|
||||||
|
'H6',
|
||||||
|
'H5',
|
||||||
|
'H4',
|
||||||
|
'H3',
|
||||||
|
'H2',
|
||||||
|
'H1',
|
||||||
|
]);
|
||||||
|
|
||||||
export default function getRichElementValue(node: HTMLElement, lines: string[], line: string[], selNode?: Node, selOffset?: number, entities?: MessageEntity[], offset = {offset: 0}) {
|
export default function getRichElementValue(node: HTMLElement, lines: string[], line: string[], selNode?: Node, selOffset?: number, entities?: MessageEntity[], offset = {offset: 0}) {
|
||||||
if(node.nodeType === 3) { // TEXT
|
if(node.nodeType === 3) { // TEXT
|
||||||
const nodeValue = node.nodeValue;
|
let nodeValue = node.nodeValue;
|
||||||
|
|
||||||
|
const tabulation = node.parentElement?.closest(tabulationMatch + ', [contenteditable]');
|
||||||
|
if(tabulation?.getAttribute('contenteditable') === null) {
|
||||||
|
nodeValue += ' ';
|
||||||
|
// line.push('\t');
|
||||||
|
// ++offset.offset;
|
||||||
|
}
|
||||||
|
|
||||||
if(selNode === node) {
|
if(selNode === node) {
|
||||||
line.push(nodeValue.substr(0, selOffset) + '\x01' + nodeValue.substr(selOffset));
|
line.push(nodeValue.substr(0, selOffset) + '\x01' + nodeValue.substr(selOffset));
|
||||||
@ -57,35 +94,44 @@ export default function getRichElementValue(node: HTMLElement, lines: string[],
|
|||||||
line.push(nodeValue);
|
line.push(nodeValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(entities && nodeValue.trim()) {
|
if(entities && nodeValue.length) {
|
||||||
if(node.parentNode) {
|
if(node.parentNode) {
|
||||||
const parentElement = node.parentElement;
|
const parentElement = node.parentElement;
|
||||||
|
|
||||||
|
// let closestTag: MarkdownTag, closestElementByTag: Element, closestDepth = Infinity;
|
||||||
for(const type in markdownTags) {
|
for(const type in markdownTags) {
|
||||||
const tag = markdownTags[type as MarkdownType];
|
const tag = markdownTags[type as MarkdownType];
|
||||||
const closest = parentElement.closest(tag.match + ', [contenteditable]');
|
const closest = parentElement.closest(tag.match + ', [contenteditable]');
|
||||||
if(closest && closest.getAttribute('contenteditable') === null) {
|
if(closest?.getAttribute('contenteditable') !== null) {
|
||||||
if(tag.entityName === 'messageEntityTextUrl') {
|
/* const depth = getDepth(closest, parentElement.closest('[contenteditable]'));
|
||||||
entities.push({
|
if(closestDepth > depth) {
|
||||||
_: tag.entityName,
|
closestDepth = depth;
|
||||||
url: (parentElement as HTMLAnchorElement).href,
|
closestTag = tag;
|
||||||
offset: offset.offset,
|
closestElementByTag = closest;
|
||||||
length: nodeValue.length
|
} */
|
||||||
});
|
continue;
|
||||||
} else if(tag.entityName === 'messageEntityMentionName') {
|
}
|
||||||
entities.push({
|
|
||||||
_: tag.entityName,
|
if(tag.entityName === 'messageEntityTextUrl') {
|
||||||
offset: offset.offset,
|
entities.push({
|
||||||
length: nodeValue.length,
|
_: tag.entityName,
|
||||||
user_id: parentElement.dataset.follow.toUserId()
|
url: (closest as HTMLAnchorElement).href,
|
||||||
});
|
offset: offset.offset,
|
||||||
} else {
|
length: nodeValue.length
|
||||||
entities.push({
|
});
|
||||||
_: tag.entityName as any,
|
} else if(tag.entityName === 'messageEntityMentionName') {
|
||||||
offset: offset.offset,
|
entities.push({
|
||||||
length: nodeValue.length
|
_: tag.entityName,
|
||||||
});
|
offset: offset.offset,
|
||||||
}
|
length: nodeValue.length,
|
||||||
|
user_id: (closest as HTMLElement).dataset.follow.toUserId()
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
entities.push({
|
||||||
|
_: tag.entityName as any,
|
||||||
|
offset: offset.offset,
|
||||||
|
length: nodeValue.length
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -100,11 +146,12 @@ export default function getRichElementValue(node: HTMLElement, lines: string[],
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const isSelected = (selNode === node);
|
const isSelected = selNode === node;
|
||||||
const isBlock = node.tagName === 'DIV' || node.tagName === 'P';
|
const isBlock = BLOCK_TAG_NAMES.has(node.tagName);
|
||||||
if(isBlock && line.length || node.tagName === 'BR') {
|
if(isBlock && line.length) {
|
||||||
lines.push(line.join(''));
|
lines.push(line.join(''));
|
||||||
line.splice(0, line.length);
|
line.splice(0, line.length);
|
||||||
|
++offset.offset;
|
||||||
} else if(node instanceof HTMLImageElement) {
|
} else if(node instanceof HTMLImageElement) {
|
||||||
const alt = node.alt;
|
const alt = node.alt;
|
||||||
if(alt) {
|
if(alt) {
|
||||||
@ -130,5 +177,11 @@ export default function getRichElementValue(node: HTMLElement, lines: string[],
|
|||||||
if(isBlock && line.length) {
|
if(isBlock && line.length) {
|
||||||
lines.push(line.join(''));
|
lines.push(line.join(''));
|
||||||
line.splice(0, line.length);
|
line.splice(0, line.length);
|
||||||
|
++offset.offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(node.tagName === 'P') {
|
||||||
|
lines.push('');
|
||||||
|
++offset.offset;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,15 @@ export default function getRichValue(field: HTMLElement, withEntities = true) {
|
|||||||
let value = lines.join('\n');
|
let value = lines.join('\n');
|
||||||
value = value.replace(/\u00A0/g, ' ');
|
value = value.replace(/\u00A0/g, ' ');
|
||||||
|
|
||||||
if(entities) {
|
if(entities?.length) {
|
||||||
|
// ! cannot do that here because have the same check before the sending in RichTextProcessor.parseMarkdown
|
||||||
|
/* const entity = entities[entities.length - 1];
|
||||||
|
const length = value.length;
|
||||||
|
const trimmedLength = value.trimRight().length;
|
||||||
|
if(length !== trimmedLength) {
|
||||||
|
entity.length -= length - trimmedLength;
|
||||||
|
} */
|
||||||
|
|
||||||
RichTextProcessor.combineSameEntities(entities);
|
RichTextProcessor.combineSameEntities(entities);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -345,9 +345,10 @@ namespace RichTextProcessor {
|
|||||||
entities.splice(0, entities.length);
|
entities.splice(0, entities.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!entities.length && !noTrim) {
|
// ! idk what it was here for
|
||||||
newText = newText.trim();
|
// if(!entities.length && !noTrim) {
|
||||||
}
|
// newText = newText.trim();
|
||||||
|
// }
|
||||||
|
|
||||||
mergeEntities(currentEntities, entities);
|
mergeEntities(currentEntities, entities);
|
||||||
combineSameEntities(currentEntities);
|
combineSameEntities(currentEntities);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user