fix: image markdown & scrollbar
- image markdown 的图片链接中如果包含其它字符会无法命中 - 滚动条在视窗大小改变后无法正确响应 - 删除操作后将滚动到光标位置处
This commit is contained in:
parent
2373d36786
commit
adfc8ef90f
|
@ -128,7 +128,7 @@ export const pluginConfig: { [key: string]: PluginOptions } = {
|
|||
// 减去大纲的宽度
|
||||
const width = editorLeft - $('.data-toc-wrapper').width();
|
||||
// 留 16px 的间隔
|
||||
return width <= 0 ? 100 : width - 16;
|
||||
return width <= 0 ? 0 : width - 16;
|
||||
},
|
||||
maxRightWidth: () => {
|
||||
// 编辑区域位置
|
||||
|
@ -139,7 +139,7 @@ export const pluginConfig: { [key: string]: PluginOptions } = {
|
|||
// 减去评论区域的宽度
|
||||
const width = editorRigth - $('.doc-comment-layer').width();
|
||||
// 留 16px 的间隔
|
||||
return width <= 0 ? 100 : width - 16;
|
||||
return width <= 0 ? 0 : width - 16;
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
@ -399,14 +399,20 @@ class ChangeModel implements ChangeInterface {
|
|||
};
|
||||
}
|
||||
let nextNode = firstNode.next();
|
||||
let beforeNode = firstNode;
|
||||
nodeApi.insert(firstNode, range);
|
||||
while (nextNode && !nodeApi.isBlock(nextNode)) {
|
||||
if (range.startContainer.nodeType === Node.TEXT_NODE)
|
||||
range.enlargeToElementNode().collapse(false);
|
||||
const newNext = nextNode.next();
|
||||
nodeApi.insert(nextNode, range);
|
||||
beforeNode.after(nextNode);
|
||||
beforeNode = nextNode;
|
||||
//nodeApi.insert(nextNode, range);
|
||||
nextNode = newNext;
|
||||
}
|
||||
if (beforeNode !== firstNode) {
|
||||
range.select(beforeNode, true).collapse(false);
|
||||
}
|
||||
if (childNodes.length === 0) {
|
||||
apply(range);
|
||||
return;
|
||||
|
|
|
@ -351,7 +351,8 @@ export default class Paste {
|
|||
const nextReg = node.get<Text>()!.splitText(match.index);
|
||||
const endReg = nextReg.splitText(match[0].length);
|
||||
node.after(nextReg);
|
||||
node.after(endReg);
|
||||
nextReg.after(endReg);
|
||||
if (!node.text()) node.remove();
|
||||
}
|
||||
}
|
||||
// 删除包含Card的 pre 标签
|
||||
|
|
|
@ -1038,22 +1038,11 @@ class NodeEntry implements NodeInterface {
|
|||
innerHeight: 0,
|
||||
innerWidth: 0,
|
||||
};
|
||||
let top, left, bottom, right;
|
||||
if (this.length > 0) {
|
||||
const element = this.get<Element>()!;
|
||||
const rect = element.getBoundingClientRect();
|
||||
top = rect.top;
|
||||
left = rect.left;
|
||||
bottom = rect.bottom;
|
||||
right = rect.right;
|
||||
} else {
|
||||
const element = this.get<Element>()!;
|
||||
const rect = element.getBoundingClientRect();
|
||||
top = rect.top;
|
||||
left = rect.left;
|
||||
bottom = rect.bottom;
|
||||
right = rect.right;
|
||||
}
|
||||
|
||||
const element = this.get<Element>()!;
|
||||
const rect = element.getBoundingClientRect();
|
||||
const { top, left, bottom, right } = rect;
|
||||
|
||||
return {
|
||||
top,
|
||||
left,
|
||||
|
@ -1081,10 +1070,15 @@ class NodeEntry implements NodeInterface {
|
|||
if (viewNode) viewNode.parentNode?.removeChild(viewNode);
|
||||
// 简单模式,只判断任一方向是否在视口内
|
||||
if (simpleMode) {
|
||||
return top <= vp.bottom || bottom <= vp.bottom;
|
||||
return (
|
||||
(top > 0 && top <= vp.bottom) ||
|
||||
(bottom > 0 && bottom <= vp.bottom)
|
||||
);
|
||||
}
|
||||
return (
|
||||
top > 0 &&
|
||||
top >= vp.top &&
|
||||
left > 0 &&
|
||||
left >= vp.left &&
|
||||
bottom <= vp.bottom &&
|
||||
right <= vp.right
|
||||
|
|
|
@ -827,7 +827,6 @@ class NodeModel implements NodeModelInterface {
|
|||
isBegin = true;
|
||||
if (text.length === 0) {
|
||||
childNode.remove();
|
||||
break;
|
||||
}
|
||||
}
|
||||
//移除末尾换行符
|
||||
|
@ -836,11 +835,11 @@ class NodeModel implements NodeModelInterface {
|
|||
childNode.text(text.substr(0, match.index));
|
||||
cloneNode.append(childNode);
|
||||
break;
|
||||
} else if (isBegin) {
|
||||
} else if (isBegin && childNode.length > 0) {
|
||||
childNode.text(text);
|
||||
}
|
||||
}
|
||||
cloneNode.append(childNode);
|
||||
if (childNode.length > 0) cloneNode.append(childNode);
|
||||
//判断下一个节点的开头是换行符,有换行符就跳出
|
||||
if (nextNode?.isText()) {
|
||||
const text = nextNode.text();
|
||||
|
|
|
@ -501,9 +501,9 @@ class Parser implements ParserInterface {
|
|||
if (result.length > 0 && /^\n+/g.test(result[0])) {
|
||||
result[0] = result[0].replace(/^\n+/g, '');
|
||||
}
|
||||
if (result.length > 0 && /^\n+/g.test(result[result.length - 1])) {
|
||||
if (result.length > 0 && /\n+$/g.test(result[result.length - 1])) {
|
||||
result[result.length - 1] = result[result.length - 1].replace(
|
||||
/^\n+/g,
|
||||
/\n+$/g,
|
||||
'',
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { EventEmitter2 } from 'eventemitter2';
|
||||
import { throttle } from 'lodash-es';
|
||||
import { debounce, throttle } from 'lodash-es';
|
||||
import { DATA_ELEMENT, UI } from '../constants';
|
||||
import { NodeInterface } from '../types';
|
||||
import { $ } from '../node';
|
||||
|
@ -37,7 +37,6 @@ class Scrollbar extends EventEmitter2 {
|
|||
private sHeight: number = 0;
|
||||
private xWidth: number = 0;
|
||||
private yHeight: number = 0;
|
||||
private maxScrollLeft: number = 0;
|
||||
#observer?: MutationObserver;
|
||||
#reverse?: boolean;
|
||||
#content?: NodeInterface;
|
||||
|
@ -120,85 +119,121 @@ class Scrollbar extends EventEmitter2 {
|
|||
}
|
||||
}
|
||||
|
||||
refresh = () => {
|
||||
getWidth() {
|
||||
const element = this.container.get<HTMLElement>();
|
||||
if (element) {
|
||||
const offsetWidth = this.#scroll?.getOffsetWidth
|
||||
? this.#scroll.getOffsetWidth(element.offsetWidth)
|
||||
: element.offsetWidth;
|
||||
if (!element) return 0;
|
||||
const offsetWidth = this.#scroll?.getOffsetWidth
|
||||
? this.#scroll.getOffsetWidth(element.offsetWidth)
|
||||
: element.offsetWidth;
|
||||
return offsetWidth;
|
||||
}
|
||||
|
||||
const { offsetHeight, scrollTop } = element;
|
||||
const contentElement = this.#content?.get<HTMLElement>();
|
||||
const sPLeft = removeUnit(this.container.css('padding-left'));
|
||||
const sPRight = removeUnit(this.container.css('padding-right'));
|
||||
const sPTop = removeUnit(this.container.css('padding-top'));
|
||||
const sPBottom = removeUnit(this.container.css('padding-bottom'));
|
||||
const scrollWidth = contentElement
|
||||
? contentElement.offsetWidth + sPLeft + sPRight
|
||||
: element.scrollWidth;
|
||||
const scrollHeight = contentElement
|
||||
? contentElement.offsetHeight + sPTop + sPBottom
|
||||
: element.scrollHeight;
|
||||
this.oWidth =
|
||||
offsetWidth -
|
||||
removeUnit(this.container.css('border-left-width')) -
|
||||
removeUnit(this.container.css('border-right-width'));
|
||||
this.oHeight =
|
||||
offsetHeight -
|
||||
removeUnit(this.container.css('border-top-width')) -
|
||||
removeUnit(this.container.css('border-bottom-width'));
|
||||
this.sWidth = scrollWidth;
|
||||
this.sHeight = scrollHeight;
|
||||
this.xWidth = Math.floor((this.oWidth * this.oWidth) / scrollWidth);
|
||||
this.yHeight = Math.floor(
|
||||
(this.oHeight * this.oHeight) / scrollHeight,
|
||||
);
|
||||
this.maxScrollLeft = scrollWidth - this.oWidth;
|
||||
if (this.x) {
|
||||
this.slideX?.css('width', this.xWidth + 'px');
|
||||
const display =
|
||||
this.oWidth - sPLeft - sPRight === this.sWidth ||
|
||||
(contentElement &&
|
||||
contentElement.offsetWidth <=
|
||||
this.oWidth - sPLeft - sPRight)
|
||||
? 'none'
|
||||
: 'block';
|
||||
this.slideX?.css('display', display);
|
||||
this.emit('display', display);
|
||||
this.shadowLeft?.css('display', display);
|
||||
this.shadowRight?.css('display', display);
|
||||
}
|
||||
if (this.y) {
|
||||
this.slideY?.css('height', this.yHeight + 'px');
|
||||
const display =
|
||||
this.oHeight - sPTop - sPBottom === this.sHeight ||
|
||||
(contentElement &&
|
||||
contentElement.offsetHeight <=
|
||||
this.oHeight - sPTop - sPBottom)
|
||||
? 'none'
|
||||
: 'block';
|
||||
this.slideY?.css('display', display);
|
||||
this.emit('display', display);
|
||||
}
|
||||
// 实际内容宽度小于容器滚动宽度(有内容删除了)
|
||||
if (
|
||||
this.x &&
|
||||
contentElement &&
|
||||
element.scrollWidth - sPLeft - sPRight >
|
||||
contentElement.offsetWidth
|
||||
) {
|
||||
let left =
|
||||
element.scrollWidth -
|
||||
sPLeft -
|
||||
sPRight -
|
||||
contentElement.offsetWidth;
|
||||
refresh = debounce(
|
||||
() => {
|
||||
const element = this.container.get<HTMLElement>();
|
||||
if (element) {
|
||||
const { offsetHeight, scrollTop } = element;
|
||||
const contentElement = this.#content?.get<HTMLElement>();
|
||||
const sPLeft = removeUnit(this.container.css('padding-left'));
|
||||
const sPRight = removeUnit(this.container.css('padding-right'));
|
||||
const sPTop = removeUnit(this.container.css('padding-top'));
|
||||
const sPBottom = removeUnit(
|
||||
this.container.css('padding-bottom'),
|
||||
);
|
||||
const scrollWidth = contentElement
|
||||
? contentElement.offsetWidth + sPLeft + sPRight
|
||||
: element.scrollWidth;
|
||||
const scrollHeight = contentElement
|
||||
? contentElement.offsetHeight + sPTop + sPBottom
|
||||
: element.scrollHeight;
|
||||
this.oWidth = this.getWidth();
|
||||
this.oHeight =
|
||||
offsetHeight -
|
||||
removeUnit(this.container.css('border-top-width')) -
|
||||
removeUnit(this.container.css('border-bottom-width'));
|
||||
this.sWidth = scrollWidth;
|
||||
this.sHeight = scrollHeight;
|
||||
this.xWidth = Math.floor(
|
||||
(this.oWidth * this.oWidth) / scrollWidth,
|
||||
);
|
||||
this.yHeight = Math.floor(
|
||||
(this.oHeight * this.oHeight) / scrollHeight,
|
||||
);
|
||||
if (this.x) {
|
||||
this.slideX?.css('width', this.xWidth + 'px');
|
||||
const display =
|
||||
this.oWidth - sPLeft - sPRight === this.sWidth ||
|
||||
(contentElement &&
|
||||
contentElement.offsetWidth <=
|
||||
this.oWidth - sPLeft - sPRight)
|
||||
? 'none'
|
||||
: 'block';
|
||||
this.slideX?.css('display', display);
|
||||
this.emit('display', display);
|
||||
this.shadowLeft?.css('display', display);
|
||||
this.shadowRight?.css('display', display);
|
||||
}
|
||||
if (this.y) {
|
||||
this.slideY?.css('height', this.yHeight + 'px');
|
||||
const display =
|
||||
this.oHeight - sPTop - sPBottom === this.sHeight ||
|
||||
(contentElement &&
|
||||
contentElement.offsetHeight <=
|
||||
this.oHeight - sPTop - sPBottom)
|
||||
? 'none'
|
||||
: 'block';
|
||||
this.slideY?.css('display', display);
|
||||
this.emit('display', display);
|
||||
}
|
||||
// 实际内容宽度小于容器滚动宽度(有内容删除了)
|
||||
if (
|
||||
this.x &&
|
||||
contentElement &&
|
||||
element.scrollWidth - sPLeft - sPRight >
|
||||
contentElement.offsetWidth
|
||||
) {
|
||||
let left =
|
||||
element.scrollWidth -
|
||||
sPLeft -
|
||||
sPRight -
|
||||
contentElement.offsetWidth;
|
||||
if (this.#scroll) {
|
||||
const { onScrollX, getScrollLeft } = this.#scroll;
|
||||
|
||||
left = getScrollLeft
|
||||
? getScrollLeft(-0) + element.scrollLeft - left
|
||||
: element.scrollLeft - left;
|
||||
if (left < 0) left = 0;
|
||||
if (onScrollX) {
|
||||
const result = onScrollX(left);
|
||||
if (result > 0) element.scrollLeft = result;
|
||||
else element.scrollLeft = 0;
|
||||
}
|
||||
this.scroll({ left });
|
||||
} else {
|
||||
element.scrollLeft -= left;
|
||||
}
|
||||
return;
|
||||
}
|
||||
// 实际内容高度小于容器滚动高度(有内容删除了)
|
||||
if (
|
||||
this.y &&
|
||||
contentElement &&
|
||||
element.scrollHeight - sPTop - sPBottom !==
|
||||
contentElement.offsetHeight
|
||||
) {
|
||||
element.scrollTop -=
|
||||
element.scrollHeight -
|
||||
sPTop -
|
||||
sPBottom -
|
||||
contentElement.offsetHeight;
|
||||
return;
|
||||
}
|
||||
const left = this.#scroll?.getScrollLeft
|
||||
? this.#scroll.getScrollLeft(element.scrollLeft)
|
||||
: element.scrollLeft;
|
||||
if (this.#scroll) {
|
||||
const { onScrollX, getScrollLeft } = this.#scroll;
|
||||
|
||||
left = getScrollLeft
|
||||
? getScrollLeft(-0) + element.scrollLeft - left
|
||||
: element.scrollLeft - left;
|
||||
if (left < 0) left = 0;
|
||||
const { onScrollX } = this.#scroll;
|
||||
if (onScrollX) {
|
||||
const result = onScrollX(left);
|
||||
if (result > 0) element.scrollLeft = result;
|
||||
|
@ -206,31 +241,14 @@ class Scrollbar extends EventEmitter2 {
|
|||
}
|
||||
this.scroll({ left });
|
||||
} else {
|
||||
element.scrollLeft -= left;
|
||||
this.reRenderX(left);
|
||||
}
|
||||
return;
|
||||
this.reRenderY(scrollTop);
|
||||
}
|
||||
// 实际内容高度小于容器滚动高度(有内容删除了)
|
||||
if (
|
||||
this.y &&
|
||||
contentElement &&
|
||||
element.scrollHeight - sPTop - sPBottom !==
|
||||
contentElement.offsetHeight
|
||||
) {
|
||||
element.scrollTop -=
|
||||
element.scrollHeight -
|
||||
sPTop -
|
||||
sPBottom -
|
||||
contentElement.offsetHeight;
|
||||
return;
|
||||
}
|
||||
const left = this.#scroll?.getScrollLeft
|
||||
? this.#scroll.getScrollLeft(element.scrollLeft)
|
||||
: element.scrollLeft;
|
||||
this.reRenderX(left);
|
||||
this.reRenderY(scrollTop);
|
||||
}
|
||||
};
|
||||
},
|
||||
50,
|
||||
{ leading: true },
|
||||
);
|
||||
|
||||
/**
|
||||
* 启用鼠标在内容节点上滚动或在移动设备使用手指滑动
|
||||
|
@ -591,13 +609,12 @@ class Scrollbar extends EventEmitter2 {
|
|||
let min = value <= 0 ? 0 : left / value;
|
||||
min = Math.min(1, min);
|
||||
this.slideX?.css('left', (this.oWidth - this.xWidth) * min + 'px');
|
||||
this.reRenderShadow(left);
|
||||
if (left === removeUnit(this.scrollBarX?.css('left') || '0'))
|
||||
return;
|
||||
this.emit('change', {
|
||||
x: left,
|
||||
y: removeUnit(this.scrollBarY?.css('top') || '0'),
|
||||
});
|
||||
this.oWidth = this.getWidth();
|
||||
this.reRenderShadow(left);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -608,7 +625,6 @@ class Scrollbar extends EventEmitter2 {
|
|||
let min = value <= 0 ? 0 : top / value;
|
||||
min = Math.min(1, min);
|
||||
this.slideY?.css('top', (this.oHeight - this.yHeight) * min + 'px');
|
||||
if (top === removeUnit(this.scrollBarX?.css('top') || '0')) return;
|
||||
this.emit('change', {
|
||||
x: removeUnit(this.scrollBarX?.css('left') || '0'),
|
||||
y: top,
|
||||
|
@ -652,6 +668,7 @@ class Scrollbar extends EventEmitter2 {
|
|||
}
|
||||
this.#observer?.disconnect();
|
||||
window.removeEventListener('resize', this.refresh);
|
||||
window.removeEventListener('scroll', this.refresh);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -78,7 +78,14 @@ class Backspace implements TypingHandleInterface {
|
|||
result = listener(event);
|
||||
if (result === false) break;
|
||||
}
|
||||
if (result === false) return;
|
||||
if (result === false) {
|
||||
if (this.engine.scrollNode)
|
||||
range.scrollIntoViewIfNeeded(
|
||||
this.engine.container,
|
||||
this.engine.scrollNode,
|
||||
);
|
||||
return;
|
||||
}
|
||||
// 范围为展开状态
|
||||
if (!range.collapsed) {
|
||||
event.preventDefault();
|
||||
|
@ -88,6 +95,11 @@ class Backspace implements TypingHandleInterface {
|
|||
}
|
||||
change.delete(range);
|
||||
change.apply(range);
|
||||
if (this.engine.scrollNode)
|
||||
range.scrollIntoViewIfNeeded(
|
||||
this.engine.container,
|
||||
this.engine.scrollNode,
|
||||
);
|
||||
return;
|
||||
} else {
|
||||
let brNode: NodeInterface | undefined = undefined;
|
||||
|
|
|
@ -685,13 +685,13 @@ export default class extends Plugin<Options> {
|
|||
if (!text) return;
|
||||
// 带跳转链接的图片
|
||||
let reg =
|
||||
/(\[!\[([^\]]{0,})\]\((https?:\/\/[^\)]{5,})\)\]\(([\S]+?)\))|(!\[([^\]]{0,})\]\((https?:\/\/[^\)]{5,})\))/;
|
||||
/(\[!\[([^\]]{0,})\]\((\S{0,})(https?:\/\/[^\)]{5,})\)\]\(([\S]+?)\))|(!\[([^\]]{0,})\]\((https?:\/\/[^\)]{5,})\))/i;
|
||||
let match = reg.exec(text);
|
||||
|
||||
if (!match) {
|
||||
// 无跳转链接的图片
|
||||
//![aomao-preview](https://user-images.githubusercontent.com/55792257/125074830-62d79300-e0f0-11eb-8d0f-bb96a7775568.png)
|
||||
reg = /(!\[([^\]]{0,})\]\((https?:\/\/[^\)]{5,})\))/;
|
||||
reg = /(!\[([^\]]{0,})\]\((\S{0,})(https?:\/\/[^\)]{5,})\))/i;
|
||||
match = reg.exec(text);
|
||||
}
|
||||
return {
|
||||
|
@ -720,9 +720,9 @@ export default class extends Plugin<Options> {
|
|||
//从匹配结束位置分割
|
||||
textNode = regNode.splitText(match[0].length);
|
||||
const isLink = match[0].startsWith('[');
|
||||
const alt = isLink ? match[2] : match[6];
|
||||
const src = isLink ? match[3] : match[7];
|
||||
const link = isLink ? match[4] : '';
|
||||
const alt = match[2] || match[6];
|
||||
const src = match[4] || match[8];
|
||||
const link = isLink ? match[5] : '';
|
||||
|
||||
const cardNode = card.replaceNode($(regNode), 'image', {
|
||||
src,
|
||||
|
|
|
@ -18,6 +18,14 @@ div[data-card-key="table"].card-selected .data-table, div[data-card-key="table"]
|
|||
background: transparent
|
||||
}
|
||||
|
||||
.am-engine [data-card-key="table"].card-selected [data-card-element="center"].data-card-background-selected {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.am-engine [data-card-key="table"].card-selected [data-card-element="center"].data-card-background-selected .table-wrapper {
|
||||
background: rgba(27, 162, 227, 0.2);
|
||||
}
|
||||
|
||||
.am-engine-mobile div[data-card-key="table"].card-activated {
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue