@textbus/core
Version:
Textbus is a rich text editor and framework that is highly customizable and extensible to achieve rich wysiwyg effects.
1,643 lines (1,622 loc) • 274 kB
JavaScript
import { Subject, distinctUntilChanged, share, map, take, microTask } from '@tanbo/stream';
export * from '@tanbo/stream';
import { InjectionToken, Injectable, Scope, ReflectiveInjector, NullInjector, normalizeProvider, Injector, Inject, Prop } from '@viewfly/core';
function makeError(name) {
return function textbusError(message) {
const error = new Error(message);
error.name = `[TextbusError: ${name}]`;
error.stack = error.stack.replace(/\n.*?(?=\n)/, '');
return error;
};
}
function replaceEmpty(s) {
const empty = '\u00a0';
return s.replace(/\s\s+/g, str => {
return ' ' + Array.from({
length: str.length - 1
}).fill(empty).join('');
}).replace(/^\s|\s$/g, empty);
}
function createBidirectionalMapping(isA) {
const a2b = new WeakMap();
const b2a = new WeakMap();
function set(key, value) {
if (get(key)) {
remove(key);
}
if (get(value)) {
remove(value);
}
if (isA(key)) {
a2b.set(key, value);
b2a.set(value, key);
}
else {
a2b.set(value, key);
b2a.set(key, value);
}
}
function get(key) {
if (isA(key)) {
return a2b.get(key);
}
return b2a.get(key);
}
function remove(key) {
if (isA(key)) {
const v = a2b.get(key);
a2b.delete(key);
b2a.delete(v);
}
else {
const v = b2a.get(key);
b2a.delete(key);
a2b.delete(v);
}
}
return {
set,
get,
remove
};
}
/******************************************************************************
Copyright (c) Microsoft Corporation.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
***************************************************************************** */
/* global Reflect, Promise, SuppressedError, Symbol */
function __decorate(decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
}
function __param(paramIndex, decorator) {
return function (target, key) { decorator(target, key, paramIndex); }
}
function __metadata(metadataKey, metadataValue) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(metadataKey, metadataValue);
}
function __awaiter(thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
}
typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
var e = new Error(message);
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
};
/**
* 根节点及原生根元素节点引用类
*/
class RootComponentRef {
}
/**
* @internal Textbus 组件列表注入 token
*/
const COMPONENT_LIST = new InjectionToken('COMPONENT_LIST');
/**
* @internal Textbus 格式列表注入 token
*/
const FORMATTER_LIST = new InjectionToken('FORMATTER_LIST');
/**
* @internal Textbus 插槽属性注入列表
*/
const ATTRIBUTE_LIST = new InjectionToken('ATTRIBUTE_LIST');
/**
* 开启 Zen Coding 支持
*/
const ZEN_CODING_DETECT = new InjectionToken('ZEN_CODING_DETECT');
/**
* 最大历史记录栈大小
*/
const HISTORY_STACK_SIZE = new InjectionToken('HISTORY_STACK_SIZE');
/**
* 是否只读
*/
const READONLY = new InjectionToken('READONLY');
class FocusManager {
}
let SelectionCorrector = class SelectionCorrector {
beforeChange(abstractSelection) {
return abstractSelection;
}
};
SelectionCorrector = __decorate([
Injectable()
], SelectionCorrector);
let firstRun = true;
/**
* Textbus 内容管理类
* Content 属于 Slot 的私有属性,在实际场景中,开发者不需在关注此类,也不需要访问或操作此类
*/
class Content {
constructor() {
Object.defineProperty(this, "data", {
enumerable: true,
configurable: true,
writable: true,
value: []
});
}
static get segmenter() {
if (Content._segmenter) {
return Content._segmenter;
}
if (Intl === null || Intl === void 0 ? void 0 : Intl.Segmenter) {
Content._segmenter = new Intl.Segmenter();
return Content._segmenter;
}
if (firstRun) {
console.warn('[Textbus: warning]: cannot found `Intl.Segmenter`, slot index will revert back to default mode.');
firstRun = false;
}
return null;
}
/**
* 内容的长度
*/
get length() {
return this.data.reduce((p, n) => p + n.length, 0);
}
/**
* 修复 index,由于 emoji 长度不固定,当 index 在 emoji 中时,操作数据会产生意外的数据
* @param index 当前的 index
* @param toEnd 当需要变更 index 时,是向后还是向前移动
*/
correctIndex(index, toEnd) {
if (index <= 0 || index >= this.length || !Content.segmenter) {
return index;
}
let i = 0;
for (const item of this.data) {
const itemLength = item.length;
if (typeof item === 'string') {
if (index > i && index < i + itemLength) {
const segments = Content.segmenter.segment(item);
let offsetIndex = 0;
for (const item of segments) {
const length = item.segment.length;
const nextOffset = offsetIndex + length;
if (nextOffset === index) {
return index;
}
if (nextOffset > index) {
if (toEnd) {
return nextOffset + i;
}
return offsetIndex + i;
}
offsetIndex = nextOffset;
}
return index;
}
}
i += itemLength;
if (i >= index) {
break;
}
}
return index;
}
/**
* 在指定下标位置插入内容
* @param index
* @param content
*/
insert(index, content) {
if (index >= this.length) {
this.append(content);
}
else {
let i = 0; // 当前内容下标
let ii = 0; // 当前数组元素下标
for (const el of this.data) {
if (index >= i) {
if (typeof el === 'string') {
if (index >= i && index < i + el.length) {
const cc = [el.slice(0, index - i), content, el.slice(index - i)].filter(i => i);
if (typeof content === 'string') {
this.data.splice(ii, 1, cc.join(''));
}
else {
this.data.splice(ii, 1, ...cc);
}
break;
}
}
else if (index === i) {
const prev = this.data[ii - 1];
if (typeof prev === 'string' && typeof content === 'string') {
this.data[ii - 1] = prev + content;
}
else if (i === 0) {
this.data.unshift(content);
}
else {
this.data.splice(ii, 0, content);
}
break;
}
}
ii++;
i += el.length;
}
}
}
/**
* 把内容添加到最后
* @param content
*/
append(content) {
const lastChildIndex = this.data.length - 1;
const lastChild = this.data[lastChildIndex];
if (typeof lastChild === 'string' && typeof content === 'string') {
this.data[lastChildIndex] = lastChild + content;
}
else {
this.data.push(content);
}
}
cut(startIndex = 0, endIndex = this.length) {
if (endIndex <= startIndex) {
return [];
}
const discardedContents = this.slice(startIndex, endIndex);
const elements = this.slice(0, startIndex).concat(this.slice(endIndex, this.length));
this.data = [];
elements.forEach(item => this.append(item));
return discardedContents;
}
slice(startIndex = 0, endIndex = this.length) {
if (startIndex >= endIndex) {
return [];
}
startIndex = this.correctIndex(startIndex, false);
endIndex = this.correctIndex(endIndex, true);
let index = 0;
const result = [];
for (const el of this.data) {
const fragmentStartIndex = index;
const len = el.length;
const fragmentEndIndex = index + len;
index += len;
if (startIndex < fragmentEndIndex && endIndex > fragmentStartIndex) {
if (typeof el === 'string') {
const min = Math.max(0, startIndex - fragmentStartIndex);
const max = Math.min(fragmentEndIndex, endIndex) - fragmentStartIndex;
result.push(el.slice(min, max));
}
else {
result.push(el);
}
}
}
return result;
}
toJSON() {
return this.data.map(i => {
if (typeof i === 'string') {
return i;
}
return i.toJSON();
});
}
indexOf(element) {
let index = 0;
for (const item of this.data) {
if (item === element) {
return index;
}
index += item.length;
}
return -1;
}
getContentAtIndex(index) {
return this.slice(index, index + 1)[0];
}
toGrid() {
const splitPoints = [0];
let index = 0;
this.data.forEach(i => {
index += i.length;
splitPoints.push(index);
});
return [...splitPoints];
}
toString() {
return this.data.map(i => {
if (typeof i === 'string') {
return i;
}
return i.toString();
}).join('');
}
}
Object.defineProperty(Content, "_segmenter", {
enumerable: true,
configurable: true,
writable: true,
value: null
});
function isVoid(data) {
return data === null || typeof data === 'undefined';
}
/**
* Textbus 格式管理类
* Format 类为 Slot 的私有属性,在实际场景中,开发者不需在关注此类,也不需要访问或操作此类
*/
class Format {
constructor(slot) {
Object.defineProperty(this, "slot", {
enumerable: true,
configurable: true,
writable: true,
value: slot
});
Object.defineProperty(this, "map", {
enumerable: true,
configurable: true,
writable: true,
value: new Map()
});
}
/**
* 将新样式合并到现有样式中
* @param formatter
* @param value
* @param background
*/
merge(formatter, value, background = false) {
let ranges = this.map.get(formatter);
if (!ranges) {
const v = value.value;
if (isVoid(v)) {
return this;
}
ranges = [value];
this.map.set(formatter, ranges);
return this;
}
const newRanges = this.normalizeFormatRange(background, ranges, value);
if (newRanges.length) {
this.map.set(formatter, newRanges);
}
else {
this.map.delete(formatter);
}
return this;
}
/**
* 将 index 后的样式起始和结束位置均增加 count 大小
* @param index
* @param count
*/
stretch(index, count) {
this.map.forEach(values => {
values.forEach(range => {
if (range.endIndex < index) {
return;
}
range.endIndex += count;
if (range.startIndex >= index) {
range.startIndex += count;
}
});
});
return this;
}
/**
* 将指定 index 位置后的样式向后平移 distance 长度
* @param index
* @param distance
*/
split(index, distance) {
Array.from(this.map).forEach(([key, formatRanges]) => {
const newRanges = [];
formatRanges.forEach(range => {
if (range.endIndex <= index) {
newRanges.push(Object.assign({}, range));
return;
}
if (range.startIndex >= index) {
newRanges.push({
startIndex: range.startIndex + distance,
endIndex: range.endIndex + distance,
value: range.value
});
return;
}
newRanges.push({
startIndex: range.startIndex,
endIndex: index,
value: range.value
}, {
startIndex: index + distance,
endIndex: distance + range.endIndex,
value: range.value
});
});
// console.log([key, formatRanges, JSON.parse(JSON.stringify(newRanges)), index, distance])
this.map.set(key, newRanges);
});
return this;
}
/**
* 从指定 index 位置的样式删除 count
* @param startIndex
* @param count
*/
shrink(startIndex, count) {
this.map.forEach(values => {
values.forEach(range => {
if (range.endIndex <= startIndex) {
return;
}
range.endIndex = Math.max(startIndex, range.endIndex - count);
if (range.startIndex > startIndex) {
range.startIndex = Math.max(startIndex, range.startIndex - count);
}
});
});
Array.from(this.map.keys()).forEach(key => {
const oldRanges = this.map.get(key);
const newRanges = this.normalizeFormatRange(false, oldRanges);
if (newRanges.length) {
this.map.set(key, newRanges);
}
else {
this.map.delete(key);
}
});
return this;
}
/**
* 提取指定范围内的样式
* @param startIndex
* @param endIndex
* @param formatter
*/
extract(startIndex, endIndex, formatter) {
const format = new Format(this.slot);
this.map.forEach((ranges, key) => {
if (formatter && !formatter.includes(key)) {
return;
}
const extractRanges = this.extractFormatRangesByFormatter(startIndex, endIndex, key);
if (extractRanges.length) {
format.map.set(key, extractRanges);
}
});
return format;
}
/**
* 生成一个重置位置的 format
* @param slot
* @param startIndex
* @param endIndex
*/
createFormatByRange(slot, startIndex, endIndex) {
const format = new Format(slot);
this.map.forEach((ranges, key) => {
const extractRanges = this.extractFormatRangesByFormatter(startIndex, endIndex, key);
if (extractRanges.length) {
format.map.set(key, extractRanges.map(i => {
i.startIndex -= startIndex;
i.endIndex -= startIndex;
return i;
}));
}
});
return format;
}
/**
* 通过 formatter 提取指定范围内的样式数据
* @param startIndex
* @param endIndex
* @param formatter
*/
extractFormatRangesByFormatter(startIndex, endIndex, formatter) {
const extractRanges = [];
const ranges = this.map.get(formatter) || [];
ranges.forEach(range => {
if (range.startIndex > endIndex || range.endIndex < startIndex) {
return;
}
const s = Math.max(range.startIndex, startIndex);
const n = Math.min(range.endIndex, endIndex);
if (s < n) {
extractRanges.push({
startIndex: s,
endIndex: n,
value: range.value
});
}
});
return extractRanges;
}
/**
* 丢弃指定范围内的样式
* @param formatter
* @param startIndex
* @param endIndex
*/
discard(formatter, startIndex, endIndex) {
const oldRanges = this.map.get(formatter);
if (oldRanges) {
this.normalizeFormatRange(false, oldRanges, {
startIndex,
endIndex,
value: null
});
}
return this;
}
extractFormatsByIndex(index) {
const formats = [];
if (index === 0) {
this.map.forEach((ranges, formatter) => {
ranges.forEach(i => {
if (i.startIndex === 0) {
formats.push([
formatter,
i.value
]);
}
});
});
}
else {
this.map.forEach((ranges, formatter) => {
ranges.forEach(i => {
if (i.startIndex < index && i.endIndex >= index) {
formats.push([
formatter,
i.value
]);
}
});
});
}
return formats;
}
toGrid() {
const splitPoints = new Set();
splitPoints.add(0);
splitPoints.add(this.slot.length);
this.map.forEach(ranges => {
ranges.forEach(item => {
splitPoints.add(item.startIndex);
splitPoints.add(item.endIndex);
});
});
return [...splitPoints].sort((a, b) => a - b);
}
toJSON() {
const json = {};
this.map.forEach((value, formatter) => {
json[formatter.name] = value.map(i => (Object.assign({}, i)));
});
return json;
}
toTree(startIndex, endIndex) {
const copyFormat = this.extract(startIndex, endIndex);
const tree = {
startIndex,
endIndex,
};
let nextStartIndex = endIndex;
let nextEndIndex = startIndex;
const formats = [];
const columnedFormats = [];
Array.from(copyFormat.map.keys()).forEach(formatter => {
const ranges = copyFormat.map.get(formatter);
ranges.forEach(range => {
if (range.startIndex === startIndex && range.endIndex === endIndex) {
if (formatter.columned) {
columnedFormats.push(Object.assign({ formatter }, range));
}
else {
formats.push(Object.assign({ formatter }, range));
copyFormat.map.delete(formatter);
}
}
else if (range.startIndex < nextStartIndex) {
nextStartIndex = range.startIndex;
nextEndIndex = range.endIndex;
}
else if (range.startIndex === nextStartIndex) {
nextEndIndex = Math.max(nextEndIndex, range.endIndex);
}
});
});
const hasChildren = copyFormat.map.size > columnedFormats.length;
if (hasChildren) {
tree.children = [];
if (startIndex < nextStartIndex) {
if (columnedFormats.length) {
const childTree = copyFormat.extract(startIndex, nextStartIndex).toTree(startIndex, nextStartIndex);
tree.children.push(childTree);
}
else {
tree.children.push({
startIndex,
endIndex: nextStartIndex
});
}
}
const push = function (tree, childTree) {
if (childTree.formats) {
tree.children.push(childTree);
}
else if (childTree.children) {
tree.children.push(...childTree.children);
}
else {
tree.children.push(childTree);
}
};
const nextTree = copyFormat.toTree(nextStartIndex, nextEndIndex);
push(tree, nextTree);
if (nextEndIndex < endIndex) {
const afterFormat = copyFormat.extract(nextEndIndex, endIndex);
const afterTree = afterFormat.toTree(nextEndIndex, endIndex);
push(tree, afterTree);
}
}
else {
formats.push(...columnedFormats);
}
if (formats.length) {
tree.formats = formats.sort((a, b) => {
return a.formatter.priority - b.formatter.priority;
});
}
return tree;
}
toArray() {
const list = [];
Array.from(this.map).forEach(i => {
const formatter = i[0];
i[1].forEach(range => {
list.push(Object.assign(Object.assign({}, range), { formatter }));
});
});
return list;
}
normalizeFormatRange(background, oldRanges, newRange) {
const length = this.slot.length;
oldRanges = oldRanges.filter(range => {
range.endIndex = Math.min(range.endIndex, length);
return range.startIndex < range.endIndex;
});
if (newRange) {
if (background) {
oldRanges.unshift(newRange);
}
else {
oldRanges.push(newRange);
}
}
if (oldRanges.length === 0) {
return [];
}
let mergedRanges = [oldRanges.at(0)];
for (let i = 1; i < oldRanges.length; i++) {
const range = oldRanges[i];
mergedRanges = Format.mergeRanges(mergedRanges, range);
}
return mergedRanges.filter(range => {
return !isVoid(range.value);
});
}
static equal(left, right) {
// 严格相等检查
if (left === right) {
return true;
}
// null 或 undefined 检查
if (left === null || left === undefined || right === null || right === undefined) {
return left === right;
}
// 类型不同直接返回 false
if (typeof left !== typeof right) {
return false;
}
// 基本类型比较
if (typeof left !== 'object') {
return left === right;
}
// 数组比较
if (Array.isArray(left) && Array.isArray(right)) {
if (left.length !== right.length) {
return false;
}
return left.every((item, index) => Format.equal(item, right[index]));
}
// 一个是数组一个不是
if (Array.isArray(left) || Array.isArray(right)) {
return false;
}
// 对象比较
const leftKeys = Object.keys(left);
const rightKeys = Object.keys(right);
if (leftKeys.length !== rightKeys.length) {
return false;
}
// 递归比较每个属性
return leftKeys.every(key => {
return rightKeys.includes(key) && Format.equal(left[key], right[key]);
});
}
static mergeRanges(ranges, newRange) {
const results = [];
let isMerged = false;
for (let i = 0; i < ranges.length; i++) {
const range = ranges[i];
if (isMerged) {
results.push(range);
continue;
}
if (range.endIndex < newRange.startIndex) {
results.push(range);
continue;
}
if (range.startIndex > newRange.endIndex) {
results.push(newRange);
results.push(range);
isMerged = true;
continue;
}
const before = range;
let last = null;
// if (before.endIndex <= newRange.endIndex) {
// i++
// }
for (; i < ranges.length; i++) {
const next = ranges[i];
if (next.startIndex <= newRange.endIndex) {
last = next;
}
else {
i--;
break;
}
}
if (!last) {
results.push(newRange);
isMerged = true;
continue;
}
if (Format.equal(before.value, newRange.value)) {
newRange.startIndex = Math.min(before.startIndex, newRange.startIndex);
newRange.endIndex = Math.max(before.endIndex, newRange.endIndex);
}
if (before.startIndex < newRange.startIndex) {
results.push({
startIndex: before.startIndex,
endIndex: newRange.startIndex,
value: before.value
});
}
if (Format.equal(last.value, newRange.value)) {
results.push({
startIndex: Math.min(last.startIndex, newRange.startIndex),
endIndex: Math.max(last.endIndex, newRange.endIndex),
value: newRange.value
});
isMerged = true;
continue;
}
results.push(newRange);
if (newRange.endIndex < last.endIndex) {
results.push({
startIndex: newRange.endIndex,
endIndex: last.endIndex,
value: last.value
});
}
isMerged = true;
}
if (!isMerged) {
results.push(newRange);
}
return results;
}
}
class EventCache {
constructor() {
Object.defineProperty(this, "listeners", {
enumerable: true,
configurable: true,
writable: true,
value: new Map()
});
}
add(eventType, callback) {
let callbacks = this.listeners.get(eventType);
if (!callbacks) {
callbacks = [];
this.listeners.set(eventType, callbacks);
}
callbacks.push(callback);
}
get(eventType) {
return this.listeners.get(eventType) || [];
}
clean(eventType) {
this.listeners.delete(eventType);
}
}
const eventCacheMap = new WeakMap();
/**
* Textbus 事件对象
*/
class Event {
get isPrevented() {
return this._isPrevented;
}
constructor(target, data) {
Object.defineProperty(this, "target", {
enumerable: true,
configurable: true,
writable: true,
value: target
});
Object.defineProperty(this, "data", {
enumerable: true,
configurable: true,
writable: true,
value: data
});
Object.defineProperty(this, "_isPrevented", {
enumerable: true,
configurable: true,
writable: true,
value: false
});
}
preventDefault() {
this._isPrevented = true;
}
}
class ContextMenuEvent extends Event {
constructor(target, getMenus) {
super(target, null);
Object.defineProperty(this, "getMenus", {
enumerable: true,
configurable: true,
writable: true,
value: getMenus
});
Object.defineProperty(this, "isStopped", {
enumerable: true,
configurable: true,
writable: true,
value: false
});
}
get stopped() {
return this.isStopped;
}
stopPropagation() {
this.isStopped = true;
}
useMenus(menus) {
this.getMenus(menus);
}
}
class GetRangesEvent extends Event {
constructor(target, getRanges) {
super(target, null);
Object.defineProperty(this, "getRanges", {
enumerable: true,
configurable: true,
writable: true,
value: getRanges
});
}
useRanges(ranges) {
this.getRanges(ranges);
}
}
function invokeListener(target, eventType, event) {
if (typeof target !== 'object' || target === null) {
return;
}
const cache = eventCacheMap.get(target);
if (cache) {
const callbacks = cache.get(eventType);
callbacks.forEach(fn => {
return fn(event);
});
if (eventType === 'onDetach') {
eventCacheMap.delete(target);
}
}
}
function makeEventHook(type) {
return function (listener) {
const context = getCurrentContext();
if (context) {
context.eventCache.add(type, listener);
}
};
}
/**
* 根据组件触发上下文菜单
* @param component
*/
function triggerContextMenu(component) {
var _a;
let comp = component;
const menuItems = [];
while (comp) {
const event = new ContextMenuEvent(comp, (menus) => {
menuItems.push(menus);
});
invokeListener(comp, 'onContextMenu', event);
if (event.stopped) {
break;
}
comp = ((_a = comp.parent) === null || _a === void 0 ? void 0 : _a.parent) || null;
}
return menuItems;
}
/**
* 当已选中组件未选中或选区不只选中已选中组件时触发
*/
const onUnselect = makeEventHook('onUnselect');
/**
* 当选区刚好选中一个组件
*/
const onSelected = makeEventHook('onSelected');
/**
* 当光标从前面进入组件
*/
const onSelectionFromFront = makeEventHook('onSelectionFromFront');
/**
* 当光标从后面进入组件
*/
const onSelectionFromEnd = makeEventHook('onSelectionFromEnd');
/**
* 组件获取焦点事件的勾子
*/
const onFocus = makeEventHook('onFocus');
/**
* 组件失去焦点事件的勾子
*/
const onBlur = makeEventHook('onBlur');
/**
* 组件或子组件获取焦点事件的勾子
*/
const onFocusIn = makeEventHook('onFocusIn');
/**
* 组件或子组件失去焦点事件的勾子
*/
const onFocusOut = makeEventHook('onFocusOut');
/**
* 组件内粘贴事件勾子
*/
const onPaste = makeEventHook('onPaste');
/**
* 组件右键菜单事件勾子
*/
const onContextMenu = makeEventHook('onContextMenu');
/**
* 组件子插槽内容删除时的勾子
*/
const onContentDelete = makeEventHook('onContentDelete');
/**
* 组件子插槽内容删除完成时的勾子
*/
const onContentDeleted = makeEventHook('onContentDeleted');
/**
* 组件子插槽换行时的勾子
*/
const onBreak = makeEventHook('onBreak');
/**
* 组件子插槽插入内容时的勾子
*/
const onContentInsert = makeEventHook('onContentInsert');
/**
* 组件子插槽插入内容后时的勾子
*/
const onContentInserted = makeEventHook('onContentInserted');
/**
* 当组件为选区公共父组件时的勾子
*/
const onGetRanges = makeEventHook('onGetRanges');
/**
* 当插槽组合输入前触发
*/
const onCompositionStart = makeEventHook('onCompositionStart');
/**
* 当插槽组合输入时触发
*/
const onCompositionUpdate = makeEventHook('onCompositionUpdate');
/**
* 当插槽组合输入结束触发
*/
const onCompositionEnd = makeEventHook('onCompositionEnd');
/**
* 当组件的父插槽数据发生更新后触发
*/
const onParentSlotUpdated = makeEventHook('onParentSlotUpdated');
/**
* 当组件插槽设置属性时触发
*/
const onSlotSetAttribute = makeEventHook('onSlotSetAttribute');
/**
* 当组件插槽应用格式时触发
*/
const onSlotApplyFormat = makeEventHook('onSlotApplyFormat');
/**
* 当组件从数据模型上剥离时触发
*/
const onDetach = makeEventHook('onDetach');
const componentErrorFn = makeError('Component');
const contextStack = [];
function getCurrentContext() {
const current = contextStack[contextStack.length - 1];
if (!current) {
throw componentErrorFn('cannot be called outside the component!');
}
return current;
}
function setup(textbus, component) {
if (component.textbus) {
return;
}
const context = {
textbus,
componentInstance: component,
eventCache: new EventCache(),
};
contextStack.push(context);
component.textbus = textbus;
onDetach(() => {
eventCacheMap.delete(component);
});
if (typeof component.setup === 'function') {
component.setup();
}
component.slots.forEach(slot => {
slot.sliceContent().forEach(i => {
if (i instanceof Component) {
setup(textbus, i);
}
});
});
eventCacheMap.set(component, context.eventCache);
contextStack.pop();
}
function isModel(value) {
return value instanceof Slot || value instanceof Component || proxyToRawCache.has(value);
}
function getObserver(v) {
return rawToProxyCache.get(v) || null;
}
function getChangeMarker(target) {
if (isModel(target)) {
return target.__changeMarker__;
}
if (target instanceof ChangeMarker) {
return target;
}
const observer = getObserver(target);
if (observer) {
return observer.__changeMarker__;
}
return null;
}
const attachErrorFn = makeError('attachError');
function attachModel(parentModel, subModel) {
var _a;
if (!isModel(subModel)) {
return;
}
const subCM = subModel.__changeMarker__;
if (subCM.parentModel === parentModel) {
return;
}
if (subCM.parentModel !== null) {
throw attachErrorFn('A data model cannot appear on two nodes.');
}
subCM.parentModel = parentModel;
if (subModel instanceof Slot) {
const textbus = (_a = subModel.parent) === null || _a === void 0 ? void 0 : _a.textbus;
if (textbus) {
subModel.sliceContent().forEach(i => {
if (i instanceof Component) {
setup(textbus, i);
}
});
}
}
}
function detachModel(...models) {
models.forEach(item => {
const changeMarker = getChangeMarker(item);
if (changeMarker) {
changeMarker.parentModel = null;
changeMarker.detach();
}
});
}
let onewayUpdate = false;
/**
* 用来标识数据模型的数据变化
*/
class ChangeMarker {
get irrevocableUpdate() {
return this._irrevocableUpdate;
}
get dirty() {
return this._dirty;
}
get changed() {
return this._changed;
}
constructor(host) {
Object.defineProperty(this, "host", {
enumerable: true,
configurable: true,
writable: true,
value: host
});
Object.defineProperty(this, "onForceChange", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "onChange", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "onChangeBefore", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "onSelfChange", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "parentModel", {
enumerable: true,
configurable: true,
writable: true,
value: null
});
Object.defineProperty(this, "detachCallbacks", {
enumerable: true,
configurable: true,
writable: true,
value: []
});
Object.defineProperty(this, "_irrevocableUpdate", {
enumerable: true,
configurable: true,
writable: true,
value: false
});
Object.defineProperty(this, "_dirty", {
enumerable: true,
configurable: true,
writable: true,
value: true
});
Object.defineProperty(this, "_changed", {
enumerable: true,
configurable: true,
writable: true,
value: true
});
Object.defineProperty(this, "_changeBefore", {
enumerable: true,
configurable: true,
writable: true,
value: true
});
Object.defineProperty(this, "changeEvent", {
enumerable: true,
configurable: true,
writable: true,
value: new Subject()
});
Object.defineProperty(this, "selfChangeEvent", {
enumerable: true,
configurable: true,
writable: true,
value: new Subject()
});
Object.defineProperty(this, "forceChangeEvent", {
enumerable: true,
configurable: true,
writable: true,
value: new Subject()
});
Object.defineProperty(this, "changeBeforeEvent", {
enumerable: true,
configurable: true,
writable: true,
value: new Subject()
});
this.onChange = this.changeEvent.asObservable();
this.onSelfChange = this.selfChangeEvent.asObservable();
this.onForceChange = this.forceChangeEvent.asObservable();
this.onChangeBefore = this.changeBeforeEvent.asObservable();
}
addDetachCallback(callback) {
this.detachCallbacks.push(callback);
}
getPaths() {
const path = this.getPathInParent();
if (path !== null) {
const parentPaths = this.parentModel.__changeMarker__.getPaths();
return [...parentPaths, path];
}
return [];
}
beforeChange() {
if (this._changeBefore) {
return;
}
this.changeBeforeEvent.next();
if (this.parentModel) {
this.parentModel.__changeMarker__.beforeChange();
}
}
forceMarkDirtied(source) {
if (this._dirty) {
return;
}
this._dirty = true;
this.forceMarkChanged(source);
}
forceMarkChanged(source) {
if (this._changed) {
return;
}
this._changed = true;
this.forceChangeEvent.next();
if (this.parentModel) {
if (!source) {
source = this.host instanceof Component ? this.host : source;
}
if (source) {
this.parentModel.__changeMarker__.forceMarkChanged(source);
}
else {
this.parentModel.__changeMarker__.forceMarkDirtied(source);
}
}
}
markAsDirtied(operation) {
this._dirty = true;
operation.irrevocable = onewayUpdate;
this._irrevocableUpdate = onewayUpdate;
if (operation.paths.length === 0) {
this.selfChangeEvent.next([...operation.apply]);
}
this.markAsChanged(operation);
this._irrevocableUpdate = false;
}
markAsChanged(operation) {
this._changed = true;
this.changeEvent.next(operation);
if (this.parentModel) {
const path = this.getPathInParent();
if (path !== null) {
operation.paths.unshift(path);
if (operation.source) {
this.parentModel.__changeMarker__.markAsChanged(operation);
}
else if (this.host instanceof Component) {
operation.source = this.host;
this.parentModel.__changeMarker__.markAsChanged(operation);
}
else {
this.parentModel.__changeMarker__.markAsDirtied(operation);
}
}
}
}
rendered() {
this._dirty = this._changed = this._changeBefore = false;
}
reset() {
this._changed = this._dirty = this._changeBefore = true;
}
detach() {
this.detachCallbacks.forEach(i => i());
if (this.host instanceof Slot) {
this.host.sliceContent().forEach(i => {
if (i instanceof Component) {
i.changeMarker.detach();
}
});
}
else if (Array.isArray(this.host)) {
this.host.forEach(i => {
const proxy = getObserver(i);
if (proxy) {
proxy.__changeMarker__.detach();
}
});
}
else if (isType(this.host, 'Object')) {
const state = this.host instanceof Component ? this.host.state : this.host;
const values = Object.values(state);
for (const value of values) {
if (value instanceof Slot) {
value.__changeMarker__.detach();
}
else {
const proxy = getObserver(toRaw(value));
if (proxy) {
proxy.__changeMarker__.detach();
}
}
}
if (this.host instanceof Component) {
invokeListener(this.host, 'onDetach');
}
}
this.detachCallbacks = [];
}
getPathInParent() {
const parentModel = this.parentModel;
if (!parentModel) {
return null;
}
if (parentModel instanceof Slot) {
return parentModel.indexOf(this.host);
}
if (Array.isArray(parentModel)) {
return parentModel.__changeMarker__.host.indexOf(this.host);
}
if (isType(parentModel, 'Object')) {
const host = parentModel.__changeMarker__.host;
const raw = host instanceof Component ? host.state : host;
const entries = Object.entries(raw);
for (const [key, value] of entries) {
if (toRaw(value) === this.host) {
return key;
}
}
}
return null;
}
}
/**
* 在回调函数内改变组件状态时,将更改的状态标记为不可撤回的
* @param fn
*/
function irrevocableUpdate(fn) {
onewayUpdate = true;
fn();
onewayUpdate = false;
}
/**
* Textbus 虚拟 DOM 文本节点
*/
class VTextNode {
constructor(textContent = '') {
Object.defineProperty(this, "textContent", {
enumerable: true,
configurable: true,
writable: true,
value: textContent
});
Object.defineProperty(this, "location", {
enumerable: true,
configurable: true,
writable: true,
value: null
});
}
}
function append(children, node) {
if (node instanceof VElement || node instanceof Component) {
children.push(node);
}
else if (node instanceof VTextNode) {
if (node.textContent) {
children.push(node);
}
}
else if (typeof node === 'string' && node.length > 0) {
children.push(new VTextNode(node));
}
else if (Array.isArray(node)) {
for (const item of node.flat()) {
append(children, item);
}
}
else if (node !== false && node !== true && node !== null && typeof node !== 'undefined') {
children.push(new VTextNode(String(node)));
}
}
function createVNode(tagName, attrs, children) {
return new VElement(tagName, attrs, children);
}
/**
* Textbus 虚拟 DOM 元素节点
*/
class VElement {
constructor(tagName, attrs, children) {
Object.defineProperty(this, "tagName", {
enumerable: true,
configurable: true,
writable: true,
value: tagName
});
Object.defineProperty(this, "children", {
enumerable: true,
configurable: true,
writable: true,
value: []
});
Object.defineProperty(this, "location", {
enumerable: true,
configurable: true,
writable: true,
value: null
});
Object.defineProperty(this, "attrs", {
enumerable: true,
configurable: true,
writable: true,
value: new Map()
});
Object.defineProperty(this, "styles", {
enumerable: true,
configurable: true,
writable: true,
value: new Map()
});
Object.defineProperty(this, "classes", {
enumerable: true,
configurable: true,
writable: true,
value: new Set()
});
Object.defineProperty(this, "listeners", {
enumerable: true,
configurable: true,
writable: true,
value: {}
});
if (attrs) {
Object.keys(attrs).forEach(key => {
if (key === 'class') {
const className = (attrs.class || '').trim();
this.classes = new Set(className ? className.split(/\s+/g) : []);
}
else if (key === 'style') {
const style = attrs.style || '';
if (typeof style === 'string') {
style.split(';').map(s => s.split(':')).forEach(v => {
if (!v[0] || !v[1]) {
return;
}
this.styles.set(v[0].trim(), v[1].trim());
});
}
else if (typeof style === 'object') {
Object.keys(style).forEach(key => {
this.styles.set(key, style[key]);
});
}
// } else if (/^on[A-Z]/.test(key)) {
// const listener = attrs![key]
// if (typeof listener === 'function') {
// this.listeners[key.replace(/^on/, '').toLowerCase()] = listener
// }
}
else {
this.attrs.set(key, attrs[key]);
}
});
}
if (children) {
children.flat(2).forEach(i => {
append(this.children, i);
});
}
}
/**
* 在最后位置添加一个子节点。
* @param newNodes
*/
appendChild(...newNodes) {
this.children.push(...newNodes);
}
}
const slotError = makeError('Slot');
var ContentType;
(function (ContentType) {
ContentType[ContentType["Text"] = 1] = "Text";
ContentType[ContentType["InlineComponent"] = 2] = "InlineComponent";
ContentType[ContentType["BlockComponent"] = 3] = "BlockComponent";
})(ContentType || (ContentType = {}));
class DeltaLite extends Array {
constructor() {
super(...arguments);
Object.defineProperty(this, "attributes", {
enumerable: true,
configurable: true,
writable: true,
value: new Map()
});
}
}
/**
* Textbus 插槽类,用于管理组件、文本及格式的增删改查
*/
class Slot {
static get emptyPlaceholder() {
// return this.schema.includes(ContentType.BlockComponent) ? '\n' : '\u200b'
return '\n';
}
/** 插槽所属的组件 */
get parent() {
let parentModel = this.__changeMarker__.parentModel;
while (parentModel) {
if (parentModel.__changeMarker__.host instanceof Component) {
return parentModel.__changeMarker__.host;
}
parentModel = parentModel.__changeMarker__.parentModel;
}
return null;
}
get parentSlot() {
var _a;
return ((_a = this.parent) === null || _a === void 0 ? void 0 : _a.changeMarker.parentModel) || null;
}
/** 插槽内容长度 */
get length() {
return this.content.length;
}
/** 插槽内容是否为空 */
get isEmpty() {
return this.length === 1 && this.getContentAtIndex(0) === Slot.emptyPlaceholder;
}
/** 插槽当前下标位置 */
get index() {
return this.isEmpty ? 0 : this._index;
}
constructor(schema, state = {}) {
/** 插槽变更标记器 */
Object.defineProperty(this, "__changeMarker__", {
enumerable: true,
configurable: true,
writable: true,
value: new ChangeMarker(this)
});
Object.defineProperty(this, "changeMarker", {
enumerable: true,
configurable: true,
writable: true,
value: this.__changeMarker__
});