feat: add card mark style

This commit is contained in:
yanmao 2021-12-24 00:45:49 +08:00
parent c2742bd908
commit 17cb84afa0
156 changed files with 1523 additions and 958 deletions

View File

View File

@ -115,5 +115,31 @@
"createdAt": 1639662957734
}
]
},
{
"id": "18HyJwZsYWIg7e7HGM",
"title": "hjhjhj",
"status": "false",
"children": [
{
"id": 10,
"username": "G-22",
"content": "gfh",
"createdAt": 1640020853940
}
]
},
{
"id": "i2FBuV1WnxFOdwi7OP",
"title": "[card:status,pNUhp]",
"status": "false",
"children": [
{
"id": 11,
"username": "G-22",
"content": ";;",
"createdAt": 1640102150645
}
]
}
]

7
api/app/data/doc.json Normal file
View File

@ -0,0 +1,7 @@
{
"id": "demo",
"content": {
"value": "<p data-id=\"pd157317-c69w7XV6\">sfsdfsd<card type=\"inline\" name=\"status\" editable=\"false\" value=\"data:%7B%22id%22%3A%22EC3Ud%22%2C%22type%22%3A%22inline%22%2C%22marks%22%3A%5B%22%3Cspan%20style%3D%5C%22background-color%3A%20rgb(212%2C%20238%2C%20252)%3B%5C%22%20class%3D%5C%22data-label-background%5C%22%3E%3Cspan%20style%3D%5C%22color%3A%20rgb(0%2C%2058%2C%20140)%3B%5C%22%3Esdfsdfsdf%3C%2Fspan%3E%3C%2Fspan%3E%22%2C%22%3Cspan%20style%3D%5C%22color%3A%20rgb(0%2C%2058%2C%20140)%3B%5C%22%3Esdfsdfsdf%3C%2Fspan%3E%22%5D%2C%22text%22%3A%22sdfsdfsdf%22%7D\"></card>sfsf</p><p data-id=\"pd157317-nj0BT1Cf\">sfsdfdsf<card type=\"inline\" name=\"mention\" editable=\"false\" value=\"data:%7B%22key%22%3A%221%22%2C%22name%22%3A%22User1%22%2C%22avatar%22%3A%22https%3A%2F%2Fcdn-image.yanmao.cc%2F12090%2Favatar%2F2020%2F06%2F07%2F1591542724-33561331-58e1-4630-8cdb-115452cc568e.jpg%3Fx-oss-process%3Dimage%2Fresize%2Cw_20%22%2C%22id%22%3A%22IXaww%22%2C%22type%22%3A%22inline%22%7D\"></card>sdfdsf</p><card type=\"block\" name=\"table\" editable=\"true\" value=\"data:%7B%22rows%22%3A3%2C%22cols%22%3A3%2C%22overflow%22%3A%7B%7D%2C%22id%22%3A%22bGJVP%22%2C%22type%22%3A%22block%22%2C%22height%22%3A105%2C%22width%22%3A1920%2C%22html%22%3A%22%3Ctable%20class%3D%5C%22data-table%5C%22%20data-id%3D%5C%22t7216feb-gnTLnZ25%5C%22%20style%3D%5C%22width%3A%201920px%3B%5C%22%3E%3Ccolgroup%20data-id%3D%5C%22c82d01ad-W3U4UTOb%5C%22%3E%3Ccol%20data-id%3D%5C%22cac3d390-XDMDqIcN%5C%22%20width%3D%5C%22640%5C%22%20%2F%3E%3Ccol%20data-id%3D%5C%22cac3d390-ImNNIOff%5C%22%20width%3D%5C%22640%5C%22%20%2F%3E%3Ccol%20data-id%3D%5C%22cac3d390-BROJ4d1q%5C%22%20width%3D%5C%22640%5C%22%20%2F%3E%3C%2Fcolgroup%3E%3Ctbody%20data-id%3D%5C%22tc1e2dd5-da12ecDI%5C%22%3E%3Ctr%20data-id%3D%5C%22t40b42a1-N7WUU3oS%5C%22%3E%3Ctd%20data-id%3D%5C%22t5815cab-0aFjlsP3%5C%22%3E%3Cp%20data-id%3D%5C%22pd157317-VvDRVwBI%5C%22%3E%3Cbr%20%2F%3E%3C%2Fp%3E%3C%2Ftd%3E%3Ctd%20data-id%3D%5C%22t5815cab-Uc8CbB8e%5C%22%3E%3Cp%20data-id%3D%5C%22pd157317-xXdePiVJ%5C%22%3E%3Cbr%20%2F%3E%3C%2Fp%3E%3C%2Ftd%3E%3Ctd%20data-id%3D%5C%22t5815cab-frDM1RSL%5C%22%20class%3D%5C%22table-last-row%5C%22%3E%3Cp%20data-id%3D%5C%22pd157317-M0Rjbb6i%5C%22%3E%3Cbr%20%2F%3E%3C%2Fp%3E%3C%2Ftd%3E%3C%2Ftr%3E%3Ctr%20data-id%3D%5C%22t40b42a1-QVl1UkpW%5C%22%3E%3Ctd%20data-id%3D%5C%22t5815cab-elPYDGuP%5C%22%3E%3Cp%20data-id%3D%5C%22pd157317-WnGOLVVF%5C%22%3E%3Cbr%20%2F%3E%3C%2Fp%3E%3C%2Ftd%3E%3Ctd%20data-id%3D%5C%22t5815cab-eEZuHkN4%5C%22%3E%3Cp%20data-id%3D%5C%22pd157317-fGRagXZj%5C%22%3E%3Cbr%20%2F%3E%3C%2Fp%3E%3C%2Ftd%3E%3Ctd%20data-id%3D%5C%22t5815cab-MAcOswh8%5C%22%20class%3D%5C%22table-last-row%5C%22%3E%3Cp%20data-id%3D%5C%22pd157317-deg74bk8%5C%22%3E%3Cbr%20%2F%3E%3C%2Fp%3E%3C%2Ftd%3E%3C%2Ftr%3E%3Ctr%20data-id%3D%5C%22t40b42a1-6iAPsUdY%5C%22%3E%3Ctd%20data-id%3D%5C%22t5815cab-xO1zh3kR%5C%22%20class%3D%5C%22table-last-column%5C%22%3E%3Cp%20data-id%3D%5C%22pd157317-E4n4Vkrn%5C%22%3E%3Cbr%20%2F%3E%3C%2Fp%3E%3C%2Ftd%3E%3Ctd%20data-id%3D%5C%22t5815cab-pOEDcevu%5C%22%20class%3D%5C%22table-last-column%5C%22%3E%3Cp%20data-id%3D%5C%22pd157317-YJTRLv7b%5C%22%3E%3Cbr%20%2F%3E%3C%2Fp%3E%3C%2Ftd%3E%3Ctd%20data-id%3D%5C%22t5815cab-yls1TDGj%5C%22%20class%3D%5C%22table-last-column%20table-last-row%5C%22%3E%3Cp%20data-id%3D%5C%22pd157317-mP4ASaqS%5C%22%3E%3Cbr%20%2F%3E%3C%2Fp%3E%3C%2Ftd%3E%3C%2Ftr%3E%3C%2Ftbody%3E%3C%2Ftable%3E%22%7D\"></card><p data-id=\"pd157317-h8lH5KeZ\"><br /></p>",
"paths": []
}
}

View File

@ -2,31 +2,31 @@
{
"key": "1",
"name": "User1",
"avatar": "https://cdn-image.yanmao.cc/12090/avatar/2020/06/07/1591542724-33561331-58e1-4630-8cdb-115452cc568e.jpg?x-oss-process=image/resize,w_20"
"avatar": "https://cdn-image.aomao.com/12090/avatar/2020/06/07/1591542724-33561331-58e1-4630-8cdb-115452cc568e.jpg?x-oss-process=image/resize,w_20"
},
{
"key": "2",
"name": "User2",
"avatar": "https://cdn-image.yanmao.cc/12090/avatar/2020/06/11/1591882490-9214e658-9f35-4502-a117-5f0dee5408cc.jpg?x-oss-process=image/resize,w_20"
"avatar": "https://cdn-image.aomao.com/12090/avatar/2020/06/11/1591882490-9214e658-9f35-4502-a117-5f0dee5408cc.jpg?x-oss-process=image/resize,w_20"
},
{
"key": "3",
"name": "User3",
"avatar": "https://cdn-image.yanmao.cc/12090/avatar/2020/06/11/1591882312-f62179ae-a80d-4064-8e53-f0857fd8e279.jpg?x-oss-process=image/resize,w_20"
"avatar": "https://cdn-image.aomao.com/12090/avatar/2020/06/11/1591882312-f62179ae-a80d-4064-8e53-f0857fd8e279.jpg?x-oss-process=image/resize,w_20"
},
{
"key": "4",
"name": "Test1",
"avatar": "https://cdn-image.yanmao.cc/13113/avatar/2020/09/19/1600521012-4a15d635-b3a1-4049-80cb-2100f597ba33.jpg?x-oss-process=image/resize,w_20"
"avatar": "https://cdn-image.aomao.com/13113/avatar/2020/09/19/1600521012-4a15d635-b3a1-4049-80cb-2100f597ba33.jpg?x-oss-process=image/resize,w_20"
},
{
"key": "5",
"name": "Test2",
"avatar": "https://cdn-image.yanmao.cc/13190/avatar/2020/10/20/1603170154-3ba3ef3b-e0a2-42d3-aa93-c8318342fb3f.jpg?x-oss-process=image/resize,w_20"
"avatar": "https://cdn-image.aomao.com/13190/avatar/2020/10/20/1603170154-3ba3ef3b-e0a2-42d3-aa93-c8318342fb3f.jpg?x-oss-process=image/resize,w_20"
},
{
"key": "6",
"name": "Test3",
"avatar": "https://cdn-image.yanmao.cc/12858/avatar/2020/05/08/1588924524-8ce0793d-6463-4ba3-bb7e-67e9a75c4945.jpeg?x-oss-process=image/resize,w_20"
"avatar": "https://cdn-image.aomao.com/12858/avatar/2020/05/08/1588924524-8ce0793d-6463-4ba3-bb7e-67e9a75c4945.jpeg?x-oss-process=image/resize,w_20"
}
]

0
site-ssr/app/router.js → api/app/router.js Executable file → Normal file
View File

View File

@ -1,4 +1,3 @@
'use strict';
const path = require('path');
module.exports = (appInfo, appConfig = {}) => {
const assetsDir = (appConfig.assets && appConfig.assets.assetsDir) || '../';
@ -16,20 +15,6 @@ module.exports = (appInfo, appConfig = {}) => {
fileSize: '20mb',
whitelist: files.split(',').map((name) => name.trim()),
};
config.assets = {
publicPath: '/public',
devServer: {
command: 'yarn start-doc',
env: {
APP_ROOT: path.join(appInfo.baseDir, assetsDir),
PORT: '{port}',
BROWSER: 'none',
ESLINT: 'none',
SOCKET_SERVER: 'http://127.0.0.1:{port}',
PUBLIC_PATH: 'http://127.0.0.1:{port}',
},
},
};
config.static = {
prefix: '/',
dir: path.join(appInfo.baseDir, 'app/public'),

4
site-ssr/config/plugin.js → api/config/plugin.js Executable file → Normal file
View File

@ -1,9 +1,5 @@
'use strict';
module.exports = {
assets: {
enable: true,
package: 'egg-view-assets',
},
nunjucks: {
enable: true,
package: 'egg-view-nunjucks',

11
site-ssr/package.json → api/package.json Executable file → Normal file
View File

@ -8,7 +8,6 @@
"stop": "egg-scripts stop --title=egg-aomao-ssr",
"dev": "egg-bin dev --port=7001 --sticky",
"debug": "cross-env RM_TMPDIR=none COMPRESS=none egg-bin debug",
"start-doc": "cd ../ && yarn start",
"test": "egg-bin test",
"cov": "egg-bin cov",
"lint": "eslint .",
@ -20,10 +19,10 @@
},
"dependencies": {
"cross-env": "^5.2.0",
"egg": "^2.29.3",
"egg": "^2.33.1",
"egg-cors": "^2.2.3",
"egg-scripts": "^2.13.0",
"egg-view-assets": "^1.7.0",
"egg-view-assets": "^1.8.0",
"egg-view-nunjucks": "^2.3.0",
"fluent-ffmpeg": "^2.1.2",
"jsdom": "^16.4.0",
@ -34,12 +33,12 @@
},
"devDependencies": {
"@types/qs": "^6.5.3",
"autod": "^3.0.1",
"autod": "^3.1.2",
"autod-egg": "^1.0.0",
"babel-plugin-dva-hmr": "^0.4.0",
"babel-plugin-import": "^1.12.0",
"egg-bin": "^4.9.0",
"egg-mock": "^3.20.1",
"egg-bin": "^4.16.3",
"egg-mock": "^4.2.0",
"eslint": "^5.8.0",
"eslint-config-egg": "^7.1.0",
"redbox-react": "^1.5.0",

83
config/config.ts Normal file
View File

@ -0,0 +1,83 @@
import { defineConfig } from 'dumi';
import getNavs from './nav.config';
import getRouters from './router.config';
export default defineConfig({
title: 'AoMao Editor',
favicon: 'https://cdn-object.aomao.com/icon/shortcut.png',
logo: 'https://cdn-object.aomao.com/icon/icon.svg',
outputPath: 'docs-dist',
hash: true,
mode: 'site',
locales: [
['en-US', 'English'],
['zh-CN', '中文'],
],
ssr: {
devServerRender: false,
removeWindowInitialProps: true,
},
navs: getNavs(),
menus: {
'/zh-CN/docs': getRouters({ lang: 'zh-CN', base: '/docs' }),
'/docs': getRouters({ base: '/docs' }),
'/zh-CN/plugin': getRouters({ lang: 'zh-CN', base: '/plugin' }),
'/plugin': getRouters({ base: '/plugin' }),
'/zh-CN/api': getRouters({ lang: 'zh-CN', base: '/api' }),
'/api': getRouters({ base: '/api' }),
},
analytics: {
baidu: '285af37fc760a8f865a67cb9120bfd8f',
},
manifest: {
fileName: 'manifest.json',
},
metas: [
{
name: 'viewport',
content:
'viewport-fit=cover,width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no',
},
{
name: 'apple-mobile-web-app-capable',
content: 'yes',
},
{
name: 'apple-mobile-web-app-status-bar-style',
content: 'black',
},
{
name: 'renderer',
content: 'webkit',
},
{
name: 'keywords',
content:
'Web富文本编辑器,React富文本编辑器,Vue富文本编辑器,协作编辑器,vue-editor, react-editor, aomao-editor, rich-text-editor',
},
{
name: 'description',
content:
'一个适用于React、Vue等前端库的Web富文本编辑器。开箱即用提供几十种丰富的编辑器插件来满足大部分需求丰富的多媒体支持不仅支持图片和音视频还有卡片概念的加持可以插入嵌入式多媒体内容使用React、Vue等前端库可以在编辑器中渲染各种各样的内容。支持 Markdown 语法,内置协同编辑方案,轻量配置即可使用。',
},
],
headScripts: [
{
src: 'https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js',
'data-ad-client': 'ca-pub-3706417744839656',
} as any,
],
proxy: {
'/api/(latex|puml|graphviz|flowchart|mermaid)': {
target: 'https://g.aomao.com/',
changeOrigin: true,
pathRewrite: { '^/api': '' },
},
'/api': {
target: 'https://editor.aomao.com',
changeOrigin: true,
pathRewrite: { '^/api': '' },
},
},
// more config: https://d.umijs.org/config
});

72
config/nav.config.ts Normal file
View File

@ -0,0 +1,72 @@
export default () => {
return {
'en-US': [
{
title: 'Edit',
path: '/',
},
{
title: 'View',
path: '/view',
},
{
title: 'Docs',
path: '/docs',
},
{
title: 'Config',
path: '/config',
},
{
title: 'Plug-in',
path: '/plugin',
},
{
title: 'API',
path: '/api',
},
{
title: 'AoMao',
path: 'https://www.aomao.com',
},
{
title: 'Github',
path: 'https://github.com/yanmao-cc/am-editor',
},
],
'zh-CN': [
{
title: '编辑',
path: '/zh-CN',
},
{
title: '阅读',
path: '/zh-CN/view',
},
{
title: '文档',
path: '/zh-CN/docs',
},
{
title: '配置',
path: '/zh-CN/config',
},
{
title: '插件',
path: '/zh-CN/plugin',
},
{
title: 'API',
path: '/zh-CN/api',
},
{
title: 'AoMao',
path: 'https://www.aomao.com',
},
{
title: 'Github',
path: 'https://github.com/yanmao-cc/am-editor',
},
],
};
};

View File

@ -1,6 +1,7 @@
import { defineConfig } from 'dumi';
function getMenus(opts: { lang?: string; base: '/docs' | '/plugin' | '/api' }) {
export default (opts: {
lang?: string;
base: '/docs' | '/plugin' | '/api';
}) => {
const menus = {
'/docs': [
{
@ -235,141 +236,4 @@ function getMenus(opts: { lang?: string; base: '/docs' | '/plugin' | '/api' }) {
title: menu[`title_${opts.lang}`] || menu.title,
};
});
}
export default defineConfig({
title: 'AoMao Editor',
favicon: 'https://cdn-object.aomao.com/icon/shortcut.png',
logo: 'https://cdn-object.aomao.com/icon/icon.svg',
outputPath: 'docs-dist',
hash: true,
mode: 'site',
locales: [
['en-US', 'English'],
['zh-CN', '中文'],
],
ssr: {
devServerRender: false,
removeWindowInitialProps: true,
},
navs: {
'en-US': [
{
title: 'Edit',
path: '/',
},
{
title: 'View',
path: '/view',
},
{
title: 'Docs',
path: '/docs',
},
{
title: 'Config',
path: '/config',
},
{
title: 'Plug-in',
path: '/plugin',
},
{
title: 'API',
path: '/api',
},
{
title: 'AoMao',
path: 'https://www.aomao.com',
},
{
title: 'Github',
path: 'https://github.com/yanmao-cc/am-editor',
},
],
'zh-CN': [
{
title: '编辑',
path: '/zh-CN',
},
{
title: '阅读',
path: '/zh-CN/view',
},
{
title: '文档',
path: '/zh-CN/docs',
},
{
title: '配置',
path: '/zh-CN/config',
},
{
title: '插件',
path: '/zh-CN/plugin',
},
{
title: 'API',
path: '/zh-CN/api',
},
{
title: 'AoMao',
path: 'https://www.aomao.com',
},
{
title: 'Github',
path: 'https://github.com/yanmao-cc/am-editor',
},
],
},
menus: {
'/zh-CN/docs': getMenus({ lang: 'zh-CN', base: '/docs' }),
'/docs': getMenus({ base: '/docs' }),
'/zh-CN/plugin': getMenus({ lang: 'zh-CN', base: '/plugin' }),
'/plugin': getMenus({ base: '/plugin' }),
'/zh-CN/api': getMenus({ lang: 'zh-CN', base: '/api' }),
'/api': getMenus({ base: '/api' }),
},
analytics: {
baidu: '285af37fc760a8f865a67cb9120bfd8f',
},
manifest: {
fileName: 'manifest.json',
},
metas: [
{
name: 'viewport',
content:
'viewport-fit=cover,width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no',
},
{
name: 'apple-mobile-web-app-capable',
content: 'yes',
},
{
name: 'apple-mobile-web-app-status-bar-style',
content: 'black',
},
{
name: 'renderer',
content: 'webkit',
},
{
name: 'keywords',
content:
'Web富文本编辑器,React富文本编辑器,Vue富文本编辑器,协作编辑器,vue-editor, react-editor, aomao-editor, rich-text-editor',
},
{
name: 'description',
content:
'一个适用于React、Vue等前端库的Web富文本编辑器。开箱即用提供几十种丰富的编辑器插件来满足大部分需求丰富的多媒体支持不仅支持图片和音视频还有卡片概念的加持可以插入嵌入式多媒体内容使用React、Vue等前端库可以在编辑器中渲染各种各样的内容。支持 Markdown 语法,内置协同编辑方案,轻量配置即可使用。',
},
],
headScripts: [
{
src: 'https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js',
'data-ad-client': 'ca-pub-3706417744839656',
},
],
// more config: https://d.umijs.org/config
});
};

View File

@ -1451,7 +1451,7 @@ drawBackground?(
```ts
/**
* 获取可编辑区域选中的所有节点
* 获取卡片区域选中的所有节点
*/
getSelectionNodes?(): Array<NodeInterface>
```

View File

@ -1,21 +1,31 @@
import {
import { $ } from '@aomao/engine';
import type {
PluginEntry,
CardEntry,
PluginOptions,
NodeInterface,
$,
} from '@aomao/engine';
//引入插件 begin
import Redo from '@aomao/plugin-redo';
import type { RedoOptions } from '@aomao/plugin-redo';
import Undo from '@aomao/plugin-undo';
import type { UndoOptions } from '@aomao/plugin-undo';
import Bold from '@aomao/plugin-bold';
import type { BoldOptions } from '@aomao/plugin-bold';
import Code from '@aomao/plugin-code';
import type { CodeOptions } from '@aomao/plugin-code';
import Backcolor from '@aomao/plugin-backcolor';
import type { BackcolorOptions } from '@aomao/plugin-backcolor';
import Fontcolor from '@aomao/plugin-fontcolor';
import type { FontcolorOptions } from '@aomao/plugin-fontcolor';
import Fontsize from '@aomao/plugin-fontsize';
import type { FontsizeOptions } from '@aomao/plugin-fontsize';
import Italic from '@aomao/plugin-italic';
import type { ItalicOptions } from '@aomao/plugin-italic';
import Underline from '@aomao/plugin-underline';
import type { UnderlineOptions } from '@aomao/plugin-underline';
import Hr, { HrComponent } from '@aomao/plugin-hr';
import type { HrOptions } from '@aomao/plugin-hr';
import Tasklist, { CheckboxComponent } from '@aomao/plugin-tasklist';
import Orderedlist from '@aomao/plugin-orderedlist';
import Unorderedlist from '@aomao/plugin-unorderedlist';
@ -49,7 +59,6 @@ import {
ToolbarComponent,
fontFamilyDefaultData,
} from '@aomao/toolbar';
import { DOMAIN } from '../../config';
import ReactDOM from 'react-dom';
import Loading from '../loading';
import Empty from 'antd/es/empty';
@ -159,20 +168,19 @@ export const pluginConfig: { [key: string]: PluginOptions } = {
},
[ImageUploader.pluginName]: {
file: {
action: `${DOMAIN}/upload/image`,
action: '/api/upload/image',
headers: { Authorization: 213434 },
},
remote: {
action: `${DOMAIN}/upload/image`,
action: '/api/upload/image',
},
isRemote: (src: string) =>
src.indexOf(DOMAIN) < 0 && src.indexOf('192.168') < 0,
isRemote: (src: string) => false,
},
[FileUploader.pluginName]: {
action: `${DOMAIN}/upload/file`,
action: '/api/upload/file',
},
[VideoUploader.pluginName]: {
action: `${DOMAIN}/upload/video`,
action: '/api/upload/video',
limitSize: 1024 * 1024 * 50,
},
[Video.pluginName]: {
@ -181,14 +189,14 @@ export const pluginConfig: { [key: string]: PluginOptions } = {
},
},
[Math.pluginName]: {
action: `https://g.aomao.com/latex`,
action: '/api/latex',
parse: (res: any) => {
if (res.success) return { result: true, data: res.svg };
return { result: false };
},
},
[Mention.pluginName]: {
action: `${DOMAIN}/user/search`,
action: '/api/user/search',
onLoading: (root: NodeInterface) => {
return ReactDOM.render(<Loading />, root.get<HTMLElement>()!);
},

View File

@ -1,11 +1,6 @@
import { isServer } from '@aomao/engine';
export const IS_DEV = process.env.NODE_ENV !== 'production';
export const DOMAIN = IS_DEV
? `http://${
typeof window !== 'undefined' ? 'localhost:7001' : 'localhost:7001'
}`
: 'https://editor.aomao.com';
export const lang = (
!isServer ? window.location.href.indexOf('zh-CN') > 0 : false

View File

@ -1,17 +1,16 @@
import { Request } from '@aomao/engine';
import { DOMAIN } from '../config';
const request = new Request();
export const list = () => {
return request.ajax({
url: `${DOMAIN}/comment/list`,
url: `/api/comment/list`,
});
};
export const remove = (payload: { render_id: string; id: number }) => {
return request.ajax({
url: `${DOMAIN}/comment/remove`,
url: `/api/comment/remove`,
method: 'POST',
data: payload,
});
@ -19,7 +18,7 @@ export const remove = (payload: { render_id: string; id: number }) => {
export const updateStatus = (payload: { ids: string; status: boolean }) => {
return request.ajax({
url: `${DOMAIN}/comment/updateStatus`,
url: `/api/comment/updateStatus`,
method: 'POST',
data: payload,
});
@ -32,7 +31,7 @@ export const add = (payload: {
username: string;
}) => {
return request.ajax({
url: `${DOMAIN}/comment/add`,
url: `/api/comment/add`,
method: 'POST',
data: payload,
});
@ -44,7 +43,7 @@ export const update = (payload: {
content: string;
}) => {
return request.ajax({
url: `${DOMAIN}/comment/update`,
url: `/api/comment/update`,
method: 'POST',
data: payload,
});

View File

@ -1,11 +1,10 @@
import { Path, Request } from '@aomao/engine';
import { DOMAIN } from '../config';
const request = new Request();
export const get = () => {
return request.ajax({
url: `${DOMAIN}/doc/get`,
url: `/api/doc/get`,
});
};
@ -14,7 +13,7 @@ export const update = (payload: {
paths: Array<{ id: Array<string>; path: Array<Path> }>;
}) => {
return request.ajax({
url: `${DOMAIN}/doc/content`,
url: `/api/doc/content`,
method: 'POST',
data: {
content: payload,

View File

@ -8,7 +8,6 @@
],
"scripts": {
"start": "dumi dev",
"ssr": "cd site-ssr && yarn dev",
"docs:build": "dumi build",
"docs:deploy": "gh-pages -d docs-dist",
"build": "node ./scripts/build",

View File

@ -1,7 +1,7 @@
{
"name": "@aomao/engine",
"version": "2.6.23",
"main": "dist/index.ts",
"main": "dist/index.js",
"module": "dist/index.esm.js",
"typings": "dist/index.d.ts",
"files": [

View File

@ -28,10 +28,10 @@ import Maximize from './maximize';
import Resize from './resize';
import Toolbar from './toolbar';
import { $ } from '../node';
import { CardType } from './enum';
import { CardType, SelectStyleType } from './enum';
import { DATA_ELEMENT, UI } from '../constants';
abstract class CardEntry<T extends CardValue = {}> implements CardInterface {
abstract class CardEntry<T extends CardValue = {}> implements CardInterface<T> {
protected readonly editor: EditorInterface;
readonly root: NodeInterface;
toolbarModel?: CardToolbarInterface;
@ -49,7 +49,7 @@ abstract class CardEntry<T extends CardValue = {}> implements CardInterface {
static readonly singleSelectable: boolean;
static readonly collab: boolean = true;
static readonly focus: boolean;
static readonly selectStyleType: 'border' | 'background' = 'border';
static readonly selectStyleType: SelectStyleType = SelectStyleType.BORDER;
static readonly toolbarFollowMouse: boolean = false;
static readonly lazyRender: boolean = false;
private defaultMaximize: MaximizeInterface;
@ -81,7 +81,7 @@ abstract class CardEntry<T extends CardValue = {}> implements CardInterface {
get id() {
const value = this.getValue();
return typeof value === 'object' ? value.id : '';
return typeof value === 'object' ? value?.id || '' : '';
}
get name() {
@ -112,17 +112,17 @@ abstract class CardEntry<T extends CardValue = {}> implements CardInterface {
return !!this.root.attributes(CARD_LOADING_KEY);
}
constructor({ editor, value, root }: CardOptions) {
constructor({ editor, value, root }: CardOptions<T>) {
this.editor = editor;
const type =
value?.type || (this.constructor as CardEntryType).cardType;
const tagName = type === 'inline' ? 'span' : 'div';
this.root = root ? root : $('<'.concat(tagName, ' />'));
if (typeof value === 'string') value = decodeCardValue(value);
value = value || {};
value = value || ({} as T);
value.id = this.getId(value.id);
value.type = type;
this.setValue(value as T);
this.setValue(value);
this.defaultMaximize = new Maximize(this.editor, this);
}
@ -159,20 +159,19 @@ abstract class CardEntry<T extends CardValue = {}> implements CardInterface {
}
const currentValue = this.getValue();
if (!!currentValue?.id) delete value['id'];
const oldValue = this.getValue();
value = { ...oldValue, ...value } as T;
if (value.type && oldValue?.type !== value.type) {
value = { ...currentValue, ...value } as T;
if (value.type && currentValue?.type !== value.type) {
this.type = value.type;
}
this.root.attributes(CARD_VALUE_KEY, encodeCardValue(value));
}
// 获取 DOM 属性里的数据
getValue(): (T & { id: string }) | undefined {
getValue() {
const value = this.root.attributes(CARD_VALUE_KEY);
if (!value) return;
if (!value) return {} as T;
return decodeCardValue(value) as T & { id: string };
return decodeCardValue<T>(value);
}
/**
@ -294,9 +293,10 @@ abstract class CardEntry<T extends CardValue = {}> implements CardInterface {
resize?: boolean | (() => NodeInterface | void);
onSelect(selected: boolean): void {
const selectedClass = `data-card-${
(this.constructor as CardEntryType).selectStyleType
}-selected`;
const selectStyleType = (this.constructor as CardEntryType)
.selectStyleType;
if (selectStyleType === SelectStyleType.NONE) return;
const selectedClass = `data-card-${selectStyleType}-selected`;
const center = this.getCenter();
if (selected) {
center.addClass(selectedClass);
@ -312,9 +312,9 @@ abstract class CardEntry<T extends CardValue = {}> implements CardInterface {
},
): NodeInterface | void {
const center = this.getCenter();
if (
(this.constructor as CardEntryType).selectStyleType === 'background'
) {
const selectStyleType = (this.constructor as CardEntryType)
.selectStyleType;
if (selectStyleType === SelectStyleType.BACKGROUND) {
center.css('background-color', selected ? value!.rgb : '');
} else {
center.css('outline', selected ? '2px solid ' + value!.color : '');
@ -390,9 +390,13 @@ abstract class CardEntry<T extends CardValue = {}> implements CardInterface {
): DOMRect | RangeInterface[] | void | false;
/**
*
*
*/
getSelectionNodes?(): Array<NodeInterface>;
executeMark?(mark: NodeInterface): void;
queryMarks?(): NodeInterface[];
}
export default CardEntry;

View File

@ -11,3 +11,9 @@ export enum CardActiveTrigger {
CLICK = 'click',
MOUSE_DOWN = 'mouse_down',
}
export enum SelectStyleType {
NONE = 'none',
BACKGROUND = 'background',
BORDER = 'border',
}

View File

@ -173,22 +173,23 @@ class CardModel implements CardModelInterface {
return selector;
}
find(
find<
E extends CardValue = {},
T extends CardInterface<E> = CardInterface<E>,
>(
selector: string | Node | NodeInterface,
ignoreEditable?: boolean,
): CardInterface | undefined {
): T | undefined {
if (typeof selector !== 'string') {
const cardNode = this.closest(selector, ignoreEditable);
if (!cardNode) return;
selector = cardNode;
}
const getValue = (
node: Node | NodeInterface,
): CardValue & { id: string } => {
const getValue = (node: Node | NodeInterface): E => {
if (isNode(node)) node = $(node);
const value = node.attributes(CARD_VALUE_KEY);
return value ? decodeCardValue(value) : {};
return decodeCardValue<E>(value);
};
const cards = this.components.filter((item) => {
if (typeof selector === 'string') return item.id === selector;
@ -205,40 +206,52 @@ class CardModel implements CardModelInterface {
});
if (cards.length === 0) return;
return cards[0];
return cards[0] as T;
}
findBlock(selector: Node | NodeInterface): CardInterface | undefined {
findBlock<
E extends CardValue = {},
T extends CardInterface<E> = CardInterface<E>,
>(selector: Node | NodeInterface): T | undefined {
if (isNode(selector)) selector = $(selector);
if (!selector.get()) return;
const parent = selector.parent();
if (!parent) return;
const card = this.find(parent);
if (!card) return;
if (card.type === CardType.BLOCK) return card;
if (card.type === CardType.BLOCK) return card as T;
return this.findBlock(card.root);
}
getSingleCard(range: RangeInterface) {
let card = this.find(range.commonAncestorNode);
if (!card) card = this.getSingleSelectedCard(range);
getSingleCard<
E extends CardValue = {},
T extends CardInterface<E> = CardInterface<E>,
>(range: RangeInterface) {
let card = this.find<E, T>(range.commonAncestorNode);
if (!card) card = this.getSingleSelectedCard<E, T>(range);
return card;
}
getSingleSelectedCard(range: RangeInterface) {
getSingleSelectedCard<
E extends CardValue = {},
T extends CardInterface<E> = CardInterface<E>,
>(range: RangeInterface) {
const elements = range.findElements();
let node = elements[0];
if (elements.length === 1 && node) {
const domNode = $(node);
if (domNode.isCard()) {
return this.find(domNode);
return this.find<E, T>(domNode);
}
}
return;
}
// 插入Card
insertNode(range: RangeInterface, card: CardInterface, ...args: any) {
insertNode<
E extends CardValue = {},
T extends CardInterface<E> = CardInterface<E>,
>(range: RangeInterface, card: T, ...args: any) {
const isInline = card.type === 'inline';
const editor = this.editor;
// 范围为折叠状态时先删除内容
@ -248,7 +261,11 @@ class CardModel implements CardModelInterface {
this.gc();
const { inline, block, node } = editor;
// 插入新 Card
let marks: NodeInterface[] = [];
if (isInline) {
if (isEngine(editor) && card.executeMark) {
marks = editor.change.marks.map((mark) => mark.clone());
}
inline.insert(card.root, range);
} else {
block.insert(
@ -285,6 +302,9 @@ class CardModel implements CardModelInterface {
if (card.didInsert) {
card.didInsert();
}
marks.forEach((mark) => {
card.executeMark!(mark, true);
});
return card;
}
@ -374,7 +394,7 @@ class CardModel implements CardModelInterface {
(trigger !== CardActiveTrigger.CLICK ||
isEngine(this.editor))
) {
this.select(card);
this.select(card, event);
}
if (
!card.isEditable &&
@ -396,20 +416,22 @@ class CardModel implements CardModelInterface {
}
}
select(card: CardInterface) {
select(card: CardInterface, event?: MouseEvent) {
const editor = this.editor;
if (!isEngine(editor)) return;
if (
(card.constructor as CardEntry).singleSelectable !== false &&
(card.type !== CardType.BLOCK || !card.activated)
) {
const range = editor.change.range.get();
const range = editor.change.range.get().cloneRange();
if (
range.startNode.closest(EDITABLE_SELECTOR).length > 0 ||
(card.isEditable && range.collapsed) ||
card.isMaximize
)
return;
// 重新设置光标,不加 preventDefault 会导致设置后失效
event?.preventDefault();
const root = card.root;
const parentNode = root.parent()!;
const index = parentNode
@ -434,9 +456,12 @@ class CardModel implements CardModelInterface {
if (scrollNode) range.scrollIntoViewIfNeeded(container, scrollNode);
}
insert(name: string, value?: CardValue, ...args: any) {
insert<
E extends CardValue = {},
T extends CardInterface<E> = CardInterface<E>,
>(name: string, value?: E, ...args: any) {
if (!isEngine(this.editor)) throw 'Engine not found';
const component = this.create(name, {
const component = this.create<E, T>(name, {
value,
});
const { change } = this.editor;
@ -462,14 +487,12 @@ class CardModel implements CardModelInterface {
}
}
replace(
source: CardInterface,
name: string,
value?: CardValue,
...args: any
) {
replace<
E extends CardValue = {},
T extends CardInterface<E> = CardInterface<E>,
>(source: CardInterface, name: string, value?: E, ...args: any) {
this.remove(source.root);
return this.insert(name, value, ...args);
return this.insert<E, T>(name, value, ...args);
}
remove(selector: NodeInterface | Node | string, hasModify: boolean = true) {
@ -525,13 +548,16 @@ class CardModel implements CardModelInterface {
}
// 创建Card DOM 节点
create(
create<
E extends CardValue = {},
T extends CardInterface<E> = CardInterface<E>,
>(
name: string,
options?: {
value?: CardValue;
value?: E;
root?: NodeInterface;
},
): CardInterface {
): T {
const clazz = this.classes[name];
if (!clazz) throw ''.concat(name, ': This card does not exist');
const type = options?.value?.type || clazz.cardType;
@ -546,7 +572,7 @@ class CardModel implements CardModelInterface {
editor: this.editor,
value: options?.value,
root: options?.root,
});
}) as T;
component.root.attributes(CARD_TYPE_KEY, type);
component.root.attributes(CARD_KEY, name);
@ -601,30 +627,10 @@ class CardModel implements CardModelInterface {
reRender(...cards: Array<CardInterface>) {
if (cards.length === 0) cards = this.components;
const render = (card: CardInterface) => {
const result = card.render();
const center = card.getCenter();
if (result !== undefined) {
center.append(typeof result === 'string' ? $(result) : result);
}
if (card.contenteditable.length > 0) {
center.find(card.contenteditable.join(',')).each((node) => {
const child = $(node);
child.attributes(
'contenteditable',
!isEngine(this.editor) || this.editor.readonly
? 'false'
: 'true',
);
child.attributes(DATA_ELEMENT, EDITABLE);
});
}
card.didRender();
};
cards.forEach((card) => {
if (card.destroy) card.destroy();
card.init();
render(card);
this.renderComponent(card);
});
}

View File

@ -74,6 +74,7 @@ class Backspace {
}
return true;
}
if (event['isDelete']) return true;
// inline 卡片
if (card.type === CardType.INLINE) {
// 左侧光标

View File

@ -329,6 +329,7 @@ class ChangeModel implements ChangeInterface {
mark.repairCursor(markNode),
);
selection.move();
range.shrinkToTextNode();
this.range.select(range);
}
this.change();

View File

@ -343,7 +343,7 @@ class NativeEvent {
change.onSelect();
});
change.event.onDocument('mousedown', (e: MouseEvent | TouchEvent) => {
change.event.onDocument('mousedown', (e: MouseEvent) => {
if (!e.target) return;
const targetNode = $(e.target);
// 点击元素已被移除
@ -363,7 +363,7 @@ class NativeEvent {
}
node = node.parent();
}
card.activate(targetNode, CardActiveTrigger.MOUSE_DOWN);
card.activate(targetNode, CardActiveTrigger.MOUSE_DOWN, e);
});
change.event.onDocument('copy', (event) => {

View File

@ -1,3 +1,4 @@
import { isMarkPlugin } from './plugin';
import { ChangeInterface } from './types';
import { CommandInterface } from './types/command';
import { EditorInterface } from './types/engine';
@ -30,8 +31,15 @@ class Command implements CommandInterface {
)
return false;
// 当前激活非可编辑卡片时全部禁用
if (this.editor.card.active && !this.editor.card.active.isEditable)
if (this.editor.card.active) {
if (
(isMarkPlugin(plugin) || plugin.kind === 'plugin') &&
this.editor.card.active.executeMark
)
return true;
if (this.editor.card.active.isEditable) return true;
return false;
}
// TODO:查询当前所处位置的插件
return true;
}

View File

@ -661,13 +661,22 @@
vertical-align: bottom;
}
.am-engine span[data-card-type="inline"] span[data-card-element="left"],.am-engine-view span[data-card-type="inline"] span[data-card-element="left"],.am-engine span[data-card-type="inline"] span[data-card-element="right"],.am-engine-view span[data-card-type="inline"] span[data-card-element="right"] {
.am-engine span[data-card-type="inline"] span[data-card-element="left"],
.am-engine-view span[data-card-type="inline"] span[data-card-element="left"],
.am-engine span[data-card-type="inline"] span[data-card-element="right"],.am-engine-view span[data-card-type="inline"] span[data-card-element="right"] {
min-width: 1px;
text-align: left;
-webkit-user-select: text;
user-select: text;
}
.am-engine span[data-card-type="inline"].card-selected span[data-card-element="left"],
.am-engine-view span[data-card-type="inline"].card-selected span[data-card-element="left"],
.am-engine span[data-card-type="inline"].card-selected span[data-card-element="right"],
.am-engine-view span[data-card-type="inline"].card-selected span[data-card-element="right"] {
background: rgba(180, 213, 254, 0.5) !important;
}
.am-engine div[data-card-type="block"],.am-engine-view div[data-card-type="block"],.am-engine span[data-card-type="inline"].data-card-block,.am-engine-view span[data-card-type="inline"].data-card-block {
display: block;
}

View File

@ -376,6 +376,7 @@ class Inline implements InlineModelInterface {
change.delete(safeRange);
}
mark.split(safeRange);
this.split(safeRange);
// 插入新 Inline
node.insert(inline, safeRange)?.select(inline).collapse(false);
@ -515,12 +516,36 @@ class Inline implements InlineModelInterface {
rightNodes[0].remove();
rightNodes.splice(0, 1);
}
if (rightNodes.filter((child) => !child.isCursor()).length > 0) {
let rightContainer = rightNodes[0];
for (let i = 0; i < rightNodes.length - 1; i++) {
rightContainer = rightNodes[i];
if (!rightContainer.isCursor()) break;
}
range.setStartBefore(rightContainer);
range.collapse(true);
} else if (
leftNodes.filter((childNode) => !childNode.isCursor()).length >
0
) {
let leftContainer = leftNodes[leftNodes.length - 1];
for (let i = leftNodes.length - 1; i >= 0; i--) {
leftContainer = leftNodes[i];
if (!leftContainer.isCursor()) break;
}
range.setStartAfter(leftContainer);
range.collapse(true);
} else {
range.select(parent, true).collapse(true);
}
parent.traverse((child) => {
if (node.isInline(child)) {
this.repairCursor(child);
}
});
}
range.enlargeToElementNode();
return keelpNode;
}
/**

View File

@ -741,6 +741,130 @@ class Mark implements MarkModelInterface {
if (!range) change.apply(safeRange);
}
/**
*
* @param node
* @param mark
* @param plugin mark插件使
* @returns void false NodeInterface
*/
wrapByNode(
node: NodeInterface,
mark: NodeInterface,
plugin: MarkInterface | undefined = this.findPlugin(mark),
) {
const nodeApi = this.editor.node;
// 要包裹的节点是mark
if (nodeApi.isMark(node)) {
if (!nodeApi.isEmpty(node)) {
//找到最底层mark标签添加包裹<strong><span style="font-size:16px">abc</span></strong> ,在 span 节点中的text再添加包裹不在strong外添加包裹
let targetNode = node;
let targetChildrens = targetNode.children();
const curPlugin = this.findPlugin(targetNode);
while (
nodeApi.isMark(targetNode) &&
targetChildrens.length === 1 &&
plugin &&
curPlugin &&
plugin.mergeLeval <= curPlugin.mergeLeval
) {
const targetChild = targetChildrens.eq(0)!;
if (nodeApi.isMark(targetChild)) {
targetNode = targetChild;
targetChildrens = targetNode.children();
} else if (targetChild.isText()) {
targetNode = targetChild;
} else break;
}
nodeApi.removeZeroWidthSpace(targetNode);
let parent = targetNode.parent();
//父级和当前要包裹的节点,属性和值都相同,那就不包裹。只有属性一样,并且父节点只有一个节点那就移除父节点包裹,然后按插件情况合并值
if (targetNode.isText()) {
let result = false;
while (parent && nodeApi.isMark(parent)) {
if (this.compare(parent.clone(), mark, true)) {
result = true;
break;
} else if (
parent
.children()
.toArray()
.filter((node) => !node.isCursor()).length === 1
) {
const curPlugin = this.findPlugin(parent);
//插件一样,并且并表明要合并值
if (
plugin &&
plugin === curPlugin &&
plugin.combineValueByWrap === true
) {
nodeApi.wrap(parent, mark, true);
result = true;
break;
}
//插件一样,不合并,直接移除
else if (plugin && plugin === curPlugin) {
nodeApi.unwrap(parent);
result = false;
break;
}
}
parent = parent.parent();
}
if (result) return false;
}
// 移除目标子级内相同的插件
const allChildren = targetNode.allChildren();
allChildren.forEach((children) => {
if (children.type === getDocument().TEXT_NODE) return;
if (nodeApi.isMark(children)) {
const childPlugin = this.findPlugin(children);
if (
childPlugin === plugin &&
!plugin?.combineValueByWrap
)
nodeApi.unwrap(children);
}
});
nodeApi.wrap(targetNode, mark);
return targetNode;
} else if (node.name !== mark.name) {
node.remove();
}
} else if (node.isCard()) {
const cardComponent = this.editor.card.find(node);
if (cardComponent && cardComponent.executeMark) {
return cardComponent.executeMark(mark, true);
}
} else if (node.isText() && !nodeApi.isEmpty(node)) {
nodeApi.removeZeroWidthSpace(node);
const parent = node.parent();
//父级和当前要包裹的节点,属性和值都相同,那就不包裹。只有属性一样,并且父节点只有一个节点那就移除父节点包裹,然后按插件情况合并值
if (parent && nodeApi.isMark(parent)) {
if (this.compare(parent.clone(), mark, true)) return false;
if (parent.children().length === 1) {
const plugin = this.findPlugin(mark);
const curPlugin = this.findPlugin(parent);
//插件一样,并且并表明要合并值
if (
plugin &&
plugin === curPlugin &&
plugin.combineValueByWrap === true
) {
nodeApi.wrap(parent, mark, true);
return parent;
}
//插件一样,不合并,直接移除
else if (plugin && plugin === curPlugin)
nodeApi.unwrap(parent);
}
}
nodeApi.wrap(node, mark);
return node;
}
return;
}
/**
* mark标签
* @param mark mark标签
@ -875,160 +999,30 @@ class Mark implements MarkModelInterface {
started = false;
return false;
}
const childIsMark = nodeApi.isMark(child);
const result = this.wrapByNode(child, mark, plugin);
// 要包裹的节点是mark
if (nodeApi.isMark(child)) {
if (!nodeApi.isEmpty(child)) {
//找到最底层mark标签添加包裹<strong><span style="font-size:16px">abc</span></strong> ,在 span 节点中的text再添加包裹不在strong外添加包裹
let targetNode = child;
let targetChildrens = targetNode.children();
const curPlugin =
this.findPlugin(targetNode);
while (
nodeApi.isMark(targetNode) &&
targetChildrens.length === 1 &&
plugin &&
curPlugin &&
plugin.mergeLeval <=
curPlugin.mergeLeval
) {
const targetChild =
targetChildrens.eq(0)!;
if (nodeApi.isMark(targetChild)) {
targetNode = targetChild;
targetChildrens =
targetNode.children();
} else if (targetChild.isText()) {
targetNode = targetChild;
} else break;
}
nodeApi.removeZeroWidthSpace(targetNode);
let parent = targetNode.parent();
//父级和当前要包裹的节点,属性和值都相同,那就不包裹。只有属性一样,并且父节点只有一个节点那就移除父节点包裹,然后按插件情况合并值
if (targetNode.isText()) {
let result = false;
while (
parent &&
nodeApi.isMark(parent)
) {
if (
this.compare(
parent.clone(),
mark,
true,
)
) {
result = true;
break;
} else if (
parent
.children()
.toArray()
.filter(
(node) =>
!node.isCursor(),
).length === 1
) {
const curPlugin =
this.findPlugin(parent);
//插件一样,并且并表明要合并值
if (
plugin &&
plugin === curPlugin &&
plugin.combineValueByWrap ===
true
) {
nodeApi.wrap(
parent,
mark,
true,
);
result = true;
break;
}
//插件一样,不合并,直接移除
else if (
plugin &&
plugin === curPlugin
) {
nodeApi.unwrap(parent);
result = false;
break;
}
}
parent = parent.parent();
}
if (result) return true;
}
// 移除目标子级内相同的插件
const allChildren =
targetNode.allChildren();
allChildren.forEach((children) => {
if (
children.type ===
getDocument().TEXT_NODE
if (
result &&
typeof result !== 'boolean' &&
childIsMark
) {
if (
!isEditable &&
selection?.focus &&
result
.find(
`[data-element="${selection.focus.attributes(
DATA_ELEMENT,
)}"]`,
)
return;
if (nodeApi.isMark(children)) {
const childPlugin =
this.findPlugin(children);
if (
childPlugin === plugin &&
!plugin?.combineValueByWrap
)
nodeApi.unwrap(children);
}
});
nodeApi.wrap(targetNode, mark);
if (
!isEditable &&
selection?.focus &&
targetNode
.find(
`[data-element="${selection.focus.attributes(
DATA_ELEMENT,
)}"]`,
)
.equal(selection.focus)
) {
started = false;
return false;
}
return true;
} else if (child.name !== mark.name) {
child.remove();
.equal(selection.focus)
) {
started = false;
return false;
}
}
if (child.isText() && !nodeApi.isEmpty(child)) {
nodeApi.removeZeroWidthSpace(child);
const parent = child.parent();
//父级和当前要包裹的节点,属性和值都相同,那就不包裹。只有属性一样,并且父节点只有一个节点那就移除父节点包裹,然后按插件情况合并值
if (parent && nodeApi.isMark(parent)) {
if (
this.compare(parent.clone(), mark, true)
)
return true;
if (parent.children().length === 1) {
const plugin = this.findPlugin(mark);
const curPlugin =
this.findPlugin(parent);
//插件一样,并且并表明要合并值
if (
plugin &&
plugin === curPlugin &&
plugin.combineValueByWrap === true
) {
nodeApi.wrap(parent, mark, true);
return true;
}
//插件一样,不合并,直接移除
else if (plugin && plugin === curPlugin)
nodeApi.unwrap(parent);
}
}
nodeApi.wrap(child, mark);
}
if (typeof result !== 'undefined') return true;
}
} else {
started = true;
@ -1110,7 +1104,7 @@ class Mark implements MarkModelInterface {
*/
merge(range?: RangeInterface): void {
if (!isEngine(this.editor)) return;
const { change, node } = this.editor;
const { change } = this.editor;
const safeRange = range || change.range.toTrusty();
const marks = this.findMarks(safeRange);
if (marks.length === 0) {
@ -1122,6 +1116,73 @@ class Mark implements MarkModelInterface {
safeRange.handleBr();
if (!range) change.apply(safeRange);
}
/**
* mark
* @param nodes
* @param removeMark mark样式
*/
unwrapByNodes(
nodes: NodeInterface[],
removeMark?: NodeInterface | Array<NodeInterface>,
) {
// 清除 Mark
const nodeApi = this.editor.node;
nodes.forEach((node) => {
removeMark = removeMark as
| NodeInterface
| NodeInterface[]
| undefined;
if (
!removeMark ||
(!node.isCard() &&
(Array.isArray(removeMark)
? removeMark
: [removeMark]
).some((m) => this.compare(node, m)))
) {
nodeApi.unwrap(node);
} else if (removeMark) {
(Array.isArray(removeMark) ? removeMark : [removeMark]).forEach(
(m) => {
const styleMap = m.css();
Object.keys(styleMap).forEach((key) => {
node.css(key, '');
});
//移除符合规则的class
const removeClass = m
.get<Element>()
?.className.split(/\s+/);
if (removeClass) {
const { schema } = this.editor;
const schemas = schema.find(
(rule) => rule.name === node.name,
);
for (let i = 0; i < schemas.length; i++) {
const schemaRule = schemas[i];
removeClass.forEach((className) => {
className = className.trim();
if (className === '') return;
if (
schemaRule.attributes &&
schema.checkValue(
schemaRule.attributes,
'class',
className,
)
) {
node.removeClass(className);
}
});
}
}
},
);
} else {
node.removeAttributes('class');
node.removeAttributes('style');
}
});
}
/**
* mark包裹
* @param range
@ -1208,6 +1269,23 @@ class Mark implements MarkModelInterface {
safeRange.isPointInRange(child, 0))
) {
markNodes.push(child);
} else if (child.isCard()) {
const cardComponent =
this.editor.card.find(child);
if (
cardComponent &&
cardComponent.executeMark
) {
(Array.isArray(removeMark)
? removeMark
: [removeMark as NodeInterface]
).forEach((mark) => {
cardComponent.executeMark!(
mark,
false,
);
});
}
}
}
}
@ -1237,52 +1315,7 @@ class Mark implements MarkModelInterface {
true,
);
});
// 清除 Mark
const nodeApi = node;
markNodes.forEach((node) => {
removeMark = removeMark as NodeInterface | undefined;
if (
!removeMark ||
(!node.isCard() && this.compare(node, removeMark))
) {
nodeApi.unwrap(node);
} else if (removeMark) {
const styleMap = removeMark.css();
Object.keys(styleMap).forEach((key) => {
node.css(key, '');
});
//移除符合规则的class
const removeClass = removeMark
.get<Element>()
?.className.split(/\s+/);
if (removeClass) {
const { schema } = this.editor;
const schemas = schema.find(
(rule) => rule.name === node.name,
);
for (let i = 0; i < schemas.length; i++) {
const schemaRule = schemas[i];
removeClass.forEach((className) => {
className = className.trim();
if (className === '') return;
if (
schemaRule.attributes &&
schema.checkValue(
schemaRule.attributes,
'class',
className,
)
) {
node.removeClass(className);
}
});
}
}
} else {
node.removeAttributes('class');
node.removeAttributes('style');
}
});
this.unwrapByNodes(markNodes, removeMark);
selection?.move();
if (isEditable) {
const markNodes: NodeInterface[] = [];
@ -1468,6 +1501,11 @@ class Mark implements MarkModelInterface {
!node.attributes(CARD_ELEMENT_KEY)
) {
nodes.push(node);
} else if (node.isCard()) {
const cardComponent = this.editor.card.find(node);
if (cardComponent?.queryMarks) {
nodes.push(...cardComponent.queryMarks());
}
}
const parent = node.parent();
if (!parent) break;
@ -1514,6 +1552,16 @@ class Mark implements MarkModelInterface {
!child.attributes(CARD_ELEMENT_KEY)
) {
addNode(nodes, child);
} else if (child.isCard()) {
const cardComponent =
this.editor.card.find(child);
if (cardComponent?.queryMarks) {
cardComponent
.queryMarks()
.forEach((mark) => {
addNode(nodes, mark);
});
}
}
}
} else {

View File

@ -1,6 +1,6 @@
import { EngineInterface } from '../types/engine';
import { Attribute, Member, SelectionInterface } from '../types/ot';
import { RangePath } from '..';
import { isTransientElement, RangePath } from '..';
import { CardType } from '../card/enum';
class OTSelection implements SelectionInterface {
@ -67,7 +67,15 @@ class OTSelection implements SelectionInterface {
const activeCard = card.active;
if (activeCard && !activeCard.isEditable) {
const center = activeCard.getCenter();
if (center && center.length > 0) {
if (isTransientElement(activeCard.root)) {
const prev = activeCard.root.prev();
if (prev) {
range.select(prev, true).collapse(false);
} else {
range.setStartBefore(activeCard.root);
range.collapse(true);
}
} else if (center && center.length > 0) {
range.select(center.get()!, true);
}
} else if (

View File

@ -16,6 +16,7 @@ abstract class PluginEntry<T extends PluginOptions = {}>
}
static readonly pluginName: string;
readonly kind: string = 'plugin';
readonly name = (this.constructor as typeof PluginEntry).pluginName;
disabled?: boolean;
// TODO:disabledPlugins: Array<string> = [];
/**

View File

@ -3,6 +3,7 @@ import {
ElementPluginInterface,
NodeInterface,
ConversionData,
PluginInterface,
} from '../types';
import {
SchemaAttributes,
@ -16,10 +17,11 @@ import { $ } from '../node';
import PluginEntry from './base';
import { isNode } from '../node/utils';
abstract class ElementPluginEntry<T extends PluginOptions = {}>
abstract class ElementPluginEntry<T extends PluginOptions>
extends PluginEntry<T>
implements ElementPluginInterface
{
readonly kind: string = 'element';
/**
*
*/
@ -242,6 +244,23 @@ abstract class ElementPluginEntry<T extends PluginOptions = {}>
* b > strong
*/
conversion?(): ConversionData;
/**
*
* @param args
* @returns
*/
createElement(...args: any) {
const markNode = $(`<${this.tagName} />`);
this.setStyle(markNode, ...args);
this.setAttributes(markNode, ...args);
return markNode;
}
}
export default ElementPluginEntry;
export const isElementPlugin = (
plugin: PluginInterface,
): plugin is ElementPluginInterface => {
return plugin.kind === 'element';
};

View File

@ -1,21 +1,23 @@
import { EditorInterface } from '../types/engine';
import {
ElementPluginInterface,
PluginEntry,
PluginInterface,
PluginModelInterface,
PluginOptions,
} from '../types/plugin';
import Plugin from './base';
import ElementPlugin from './element';
import ElementPlugin, { isElementPlugin } from './element';
import BlockPlugin, { isBlockPlugin } from './block';
import InlinePlugin, { isInlinePlugin } from './inline';
import ListPlugin from './list';
import MarkPlugin, { isMarkPlugin } from './mark';
import { isEngine } from '../utils';
import { BlockInterface, InlineInterface, MarkInterface } from 'src';
class PluginModel implements PluginModelInterface {
protected data: { [k: string]: PluginEntry } = {};
components: { [k: string]: PluginInterface } = {};
protected data: Record<string, PluginEntry> = {};
components: Record<string, PluginInterface<PluginOptions>> = {};
protected editor: EditorInterface;
constructor(editor: EditorInterface) {
this.editor = editor;
@ -43,6 +45,41 @@ class PluginModel implements PluginModelInterface {
}
}
findPlugin(pluginName: string) {
const plugin = this.components[pluginName];
return plugin;
}
findElementPlugin(pluginName: string) {
const plugin = this.findPlugin(pluginName);
if (isElementPlugin(plugin)) {
return plugin as ElementPluginInterface;
}
return;
}
findMarkPlugin(pluginName: string) {
const plugin = this.findPlugin(pluginName);
if (isMarkPlugin(plugin)) {
return plugin as MarkInterface;
}
return;
}
findInlinePlugin(pluginName: string) {
const plugin = this.findPlugin(pluginName);
if (isInlinePlugin(plugin)) {
return plugin as InlineInterface;
}
return;
}
findBlockPlugin(pluginName: string) {
const plugin = this.findPlugin(pluginName);
if (isBlockPlugin(plugin)) {
return plugin as BlockInterface;
}
return;
}
each(
callback: (
name: string,

View File

@ -65,9 +65,7 @@ abstract class MarkEntry<T extends {} = {}>
const editor = this.editor;
if (!isEngine(editor)) return;
const { change, mark } = editor;
const markNode = $(`<${this.tagName} />`);
this.setStyle(markNode, ...args);
this.setAttributes(markNode, ...args);
const markNode = this.createElement(...args);
const trigger = this.isTrigger
? this.isTrigger(...args)
: !this.queryState();

View File

@ -376,8 +376,7 @@ class Range implements RangeInterface {
!node.isVoid(child) &&
(!childDom.isCard() ||
childDom.isEditableCard() ||
(childDom.closest(EDITABLE_SELECTOR).length > 0 &&
childDom.find(CARD_LEFT_SELECTOR).length > 0))
childDom.find(CARD_LEFT_SELECTOR).length > 0)
) {
this.setStart(child, 0);
}
@ -391,8 +390,7 @@ class Range implements RangeInterface {
!childDom.isCursor() &&
(!childDom.isCard() ||
childDom.isEditableCard() ||
(childDom.closest(EDITABLE_SELECTOR).length > 0 &&
childDom.find(CARD_RIGHT_SELECTOR).length > 0))
childDom.find(CARD_RIGHT_SELECTOR).length > 0)
) {
this.setEnd(child, child.childNodes.length);
}

View File

@ -7,19 +7,18 @@ import {
DropdownSwitchOptions,
ToolbarItemOptions,
} from './toolbar';
import { CardActiveTrigger, CardType } from '../card/enum';
import { CardActiveTrigger, CardType, SelectStyleType } from '../card/enum';
import { Placement } from './position';
export type CardOptions = {
export interface CardOptions<T extends CardValue = {}> {
editor: EditorInterface;
value?: CardValue;
value?: T;
root?: NodeInterface;
};
}
export type CardValue = {
id?: string;
type?: CardType;
[key: string]: any;
};
export interface CardToolbarInterface {
@ -85,9 +84,9 @@ export type CardToolbarItemOptions =
items: Array<DropdownSwitchOptions | DropdownButtonOptions>;
};
export interface CardEntry {
export interface CardEntry<T extends CardValue = {}> {
prototype: CardInterface;
new (options: CardOptions): CardInterface;
new (options: CardOptions<T>): CardInterface;
/**
*
*/
@ -119,7 +118,7 @@ export interface CardEntry {
/**
* border
*/
readonly selectStyleType: 'border' | 'background';
readonly selectStyleType: SelectStyleType;
/**
* toolbar
*/
@ -130,7 +129,7 @@ export interface CardEntry {
readonly lazyRender: boolean;
}
export interface CardInterface {
export interface CardInterface<T extends CardValue = {}> {
/**
*
*/
@ -286,11 +285,11 @@ export interface CardInterface {
*
* @param value
*/
setValue(value: Partial<CardValue>): void;
setValue(value: Partial<T>): void;
/**
*
*/
getValue(): CardValue | undefined;
getValue(): T;
/**
*
*/
@ -351,6 +350,16 @@ export interface CardInterface {
*
*/
getSelectionNodes?(): Array<NodeInterface>;
/**
* mark样式
* @param mark mark样式
* @param warp truemark样式添加到卡片节点,
*/
executeMark?(mark?: NodeInterface, warp?: boolean): void;
/**
* mark样式
*/
queryMarks?(): NodeInterface[];
}
export interface CardModel {
@ -402,36 +411,57 @@ export interface CardModelInterface {
* @param selector ID
* @param ignoreEditable
*/
find(
find<
E extends CardValue = {},
T extends CardInterface<E> = CardInterface<E>,
>(
selector: NodeInterface | Node | string,
ignoreEditable?: boolean,
): CardInterface | undefined;
): T | undefined;
/**
* Block Card
* @param selector ID
*/
findBlock(selector: Node | NodeInterface): CardInterface | undefined;
findBlock<
E extends CardValue = {},
T extends CardInterface<E> = CardInterface<E>,
>(
selector: Node | NodeInterface,
): T | undefined;
/**
*
* @param range
*/
getSingleCard(range: RangeInterface): CardInterface | undefined;
getSingleCard<
E extends CardValue = {},
T extends CardInterface<E> = CardInterface<E>,
>(
range: RangeInterface,
): T | undefined;
/**
*
* @param rang
*/
getSingleSelectedCard(rang: RangeInterface): CardInterface | undefined;
getSingleSelectedCard<
E extends CardValue = {},
T extends CardInterface<E> = CardInterface<E>,
>(
rang: RangeInterface,
): T | undefined;
/**
*
* @param range
* @param card
* @param args
*/
insertNode(
insertNode<
E extends CardValue = {},
T extends CardInterface<E> = CardInterface<E>,
>(
range: RangeInterface,
card: CardInterface,
card: T,
...args: any
): CardInterface;
): T;
/**
*
* @param card
@ -443,10 +473,10 @@ export interface CardModelInterface {
* @param name
* @param value
*/
replaceNode(
replaceNode<V extends CardValue>(
node: NodeInterface,
name: string,
value?: CardValue,
value?: Partial<V>,
): NodeInterface;
/**
*
@ -454,7 +484,14 @@ export interface CardModelInterface {
* @param value
* @param args
*/
updateNode(card: CardInterface, value: CardValue, ...args: any): void;
updateNode<
E extends CardValue = {},
T extends CardInterface<E> = CardInterface<E>,
>(
card: T,
value: Partial<E>,
...args: any
): void;
/**
*
* @param node
@ -483,16 +520,23 @@ export interface CardModelInterface {
* @param value
* @param args
*/
insert(name: string, value?: CardValue, ...args: any): CardInterface;
insert<
E extends CardValue = {},
T extends CardInterface<E> = CardInterface<E>,
>(
name: string,
value?: Partial<E>,
...args: any
): T;
/**
*
* @param selector
* @param value
* @param args
*/
update(
update<V extends CardValue = {}>(
selector: NodeInterface | Node | string,
value: CardValue,
value: Partial<V>,
...args: any
): void;
/**
@ -502,12 +546,15 @@ export interface CardModelInterface {
* @param value
* @param args
*/
replace(
replace<
E extends CardValue = {},
T extends CardInterface<E> = CardInterface<E>,
>(
source: CardInterface,
name: string,
value?: CardValue,
value?: Partial<E>,
...args: any
): CardInterface;
): T;
/**
*
* @param selector
@ -523,13 +570,16 @@ export interface CardModelInterface {
* @param name
* @param options
*/
create(
create<
E extends CardValue = {},
T extends CardInterface<E> = CardInterface<E>,
>(
name: string,
options?: {
value?: CardValue;
value?: Partial<E>;
root?: NodeInterface;
},
): CardInterface;
): T;
/**
*
* @param container

View File

@ -56,6 +56,27 @@ export interface MarkModelInterface {
* @param both mark标签两侧节点
*/
wrap(mark: NodeInterface | Node | string, range?: RangeInterface): void;
/**
*
* @param node
* @param mark
* @param plugin mark插件
* @returns void false NodeInterface
*/
wrapByNode(
node: NodeInterface,
mark: NodeInterface,
plugin?: MarkInterface,
): false | void | NodeInterface;
/**
* mark
* @param nodes
* @param removeMark mark样式
*/
unwrapByNodes(
nodes: NodeInterface[],
removeMark?: NodeInterface | Array<NodeInterface>,
): void;
/**
* mark包裹
* @param range

View File

@ -1,3 +1,4 @@
import { BlockInterface, InlineInterface, MarkInterface } from '.';
import { CardInterface } from './card';
import { ConversionData } from './conversion';
import { EditorInterface } from './engine';
@ -18,12 +19,16 @@ export type PluginOptions = {
export interface PluginEntry {
prototype: PluginInterface;
new (editor: EditorInterface, options: PluginOptions): PluginInterface;
new (
editor: EditorInterface,
options: PluginOptions,
): PluginInterface<PluginOptions>;
readonly pluginName: string;
}
export interface PluginInterface<T extends PluginOptions = {}> {
readonly kind: string;
readonly name: string;
/**
*
**/
@ -146,6 +151,12 @@ export interface ElementPluginInterface extends PluginInterface {
* b > strong
*/
conversion?(): ConversionData;
/**
*
* @param args
* @returns
*/
createElement(...args: any): NodeInterface;
}
export interface PluginModelInterface {
@ -175,4 +186,13 @@ export interface PluginModelInterface {
index?: number,
) => boolean | void,
): void;
/**
*
* @param pluginName
*/
findPlugin(pluginName: string): PluginInterface | undefined;
findElementPlugin(pluginName: string): ElementPluginInterface | undefined;
findMarkPlugin(pluginName: string): MarkInterface | undefined;
findInlinePlugin(pluginName: string): InlineInterface | undefined;
findBlockPlugin(pluginName: string): BlockInterface | undefined;
}

View File

@ -198,26 +198,25 @@ export const removeUnit = (value: string) => {
* Card组件值编码
* @param value
*/
export const encodeCardValue = (value: any): string => {
export const encodeCardValue = <T = Record<string, any>>(value: T): string => {
let str = '';
try {
value = encodeURIComponent(JSON.stringify(value || ''));
} catch (e) {
value = '';
}
str = encodeURIComponent(JSON.stringify(value || ''));
} catch (e) {}
return 'data:'.concat(value);
return 'data:'.concat(str);
};
/**
* Card组件值解码
* @param value
*/
export const decodeCardValue = (value: string): any => {
export const decodeCardValue = <T = Record<string, any>>(value: string): T => {
try {
value = value.substr(5);
return JSON.parse(decodeURIComponent(value));
} catch (e) {
return {};
return {} as T;
}
};

View File

@ -1,7 +1,7 @@
{
"name": "@aomao/toolbar-vue",
"version": "2.6.23",
"main": "dist/index.ts",
"main": "dist/index.js",
"module": "dist/index.esm.js",
"typings": "dist/index.d.ts",
"files": [

View File

@ -6,6 +6,7 @@ import {
isHotkey,
CardType,
isServer,
CardValue,
} from '@aomao/engine';
import {
CollapseGroupProps,
@ -16,9 +17,13 @@ import { getToolbarDefaultConfig } from '../../config';
import CollapseComponent, { CollapseComponentInterface } from './collapse';
import './index.css';
export type Data = Array<CollapseGroupProps>;
type Data = Array<CollapseGroupProps>;
class ToolbarComponent extends Card<{ data: Data }> {
export interface ToolbarValue extends CardValue {
data: Data;
}
class ToolbarComponent<V extends ToolbarValue> extends Card<V> {
private keyword?: NodeInterface;
private placeholder?: NodeInterface;
private component?: CollapseComponentInterface;

View File

@ -8,13 +8,13 @@ import {
} from '@aomao/engine';
import { CollapseItemProps } from '../types';
import locales from '../locales';
import ToolbarComponent from './component';
import ToolbarComponent, { ToolbarValue } from './component';
type Config = Array<{
title: string;
items: Array<Omit<CollapseItemProps, 'engine'> | string>;
}>;
export interface Options extends PluginOptions {
export interface ToolbarOptions extends PluginOptions {
config: Config;
}
@ -39,7 +39,7 @@ const defaultConfig = (editor: EditorInterface): Config => {
];
};
class ToolbarPlugin extends Plugin<Options> {
class ToolbarPlugin<T extends ToolbarOptions> extends Plugin<T> {
static get pluginName() {
return 'toolbar';
}
@ -86,7 +86,7 @@ class ToolbarPlugin extends Plugin<Options> {
ToolbarComponent.cardName,
{},
data,
) as ToolbarComponent;
) as ToolbarComponent<ToolbarValue>;
card.setData(data);
this.editor.card.activate(card.root);
range = change.range.get();
@ -104,4 +104,5 @@ class ToolbarPlugin extends Plugin<Options> {
}
}
export { ToolbarComponent };
export type { ToolbarValue };
export default ToolbarPlugin;

View File

@ -1,7 +1,7 @@
{
"name": "@aomao/toolbar",
"version": "2.6.23",
"main": "dist/index.ts",
"main": "dist/index.js",
"module": "dist/index.esm.js",
"typings": "dist/index.d.ts",
"files": [

View File

@ -6,6 +6,7 @@ import {
isHotkey,
CardType,
isServer,
CardValue,
} from '@aomao/engine';
import { CollapseGroupProps } from '../../collapse/group';
import { CollapseItemProps } from '../../collapse/item';
@ -14,9 +15,12 @@ import { CollapseProps } from '../../types';
import CollapseComponent, { CollapseComponentInterface } from './collapse';
import './index.css';
export type Data = Array<CollapseGroupProps>;
type Data = Array<CollapseGroupProps>;
export interface ToolbarValue extends CardValue {
data: Data;
}
class ToolbarComponent extends Card {
class ToolbarComponent<T extends ToolbarValue> extends Card<T> {
private keyword?: NodeInterface;
private placeholder?: NodeInterface;
private component?: CollapseComponentInterface;
@ -95,7 +99,7 @@ class ToolbarComponent extends Card {
? collapseItem.onDisabled()
: !this.editor.command.queryEnabled(name),
});
}
} else if (typeof item === 'object') items.push(item);
});
data.push({
title,

View File

@ -9,14 +9,14 @@ import {
PluginOptions,
} from '@aomao/engine';
import { CollapseItemProps } from '../collapse/item';
import ToolbarComponent from './component';
import ToolbarComponent, { ToolbarValue } from './component';
import locales from '../locales';
type Config = Array<{
title: React.ReactNode;
items: Array<Omit<CollapseItemProps, 'engine'> | string>;
}>;
export interface Options extends PluginOptions {
export interface ToolbarOptions extends PluginOptions {
config: Config;
}
@ -42,7 +42,7 @@ const defaultConfig = (editor: EditorInterface): Config => {
];
};
class ToolbarPlugin extends Plugin<Options> {
class ToolbarPlugin<T extends ToolbarOptions> extends Plugin<T> {
static get pluginName() {
return 'toolbar';
}
@ -89,7 +89,7 @@ class ToolbarPlugin extends Plugin<Options> {
ToolbarComponent.cardName,
{},
data,
) as ToolbarComponent;
) as ToolbarComponent<ToolbarValue>;
card.setData(data);
card.root.attributes(DATA_TRANSIENT_ELEMENT, 'true');
this.editor.card.activate(card.root);
@ -108,4 +108,5 @@ class ToolbarPlugin extends Plugin<Options> {
}
}
export { ToolbarComponent };
export type { ToolbarValue };
export default ToolbarPlugin;

View File

@ -1,7 +1,7 @@
{
"name": "@aomao/plugin-alignment",
"version": "2.6.23",
"main": "dist/index.ts",
"main": "dist/index.js",
"module": "dist/index.esm.js",
"typings": "dist/index.d.ts",
"files": [

View File

@ -6,7 +6,7 @@ import {
PluginOptions,
} from '@aomao/engine';
export interface Options extends PluginOptions {
export interface AlignmentOptions extends PluginOptions {
hotkey?: {
left?: string;
center?: string;
@ -14,7 +14,7 @@ export interface Options extends PluginOptions {
justify?: string;
};
}
export default class extends ElementPlugin<Options> {
export default class<T extends AlignmentOptions> extends ElementPlugin<T> {
kind = 'block';
style = {

View File

@ -1,7 +1,7 @@
{
"name": "@aomao/plugin-backcolor",
"version": "2.6.23",
"main": "dist/index.ts",
"main": "dist/index.js",
"module": "dist/index.esm.js",
"typings": "dist/index.d.ts",
"files": [

View File

@ -1,9 +1,9 @@
import { MarkPlugin, PluginOptions } from '@aomao/engine';
export interface Options extends PluginOptions {
export interface BackcolorOptions extends PluginOptions {
hotkey?: { key: string; args: Array<string> };
}
export default class extends MarkPlugin<Options> {
export default class<T extends BackcolorOptions> extends MarkPlugin<T> {
static get pluginName() {
return 'backcolor';
}

View File

@ -1,7 +1,7 @@
{
"name": "@aomao/plugin-bold",
"version": "2.6.23",
"main": "dist/index.ts",
"main": "dist/index.js",
"module": "dist/index.esm.js",
"typings": "dist/index.d.ts",
"files": [

View File

@ -1,10 +1,10 @@
import { MarkPlugin, PluginOptions } from '@aomao/engine';
export interface Options extends PluginOptions {
export interface BoldOptions extends PluginOptions {
hotkey?: string | Array<string>;
markdown?: string;
}
export default class extends MarkPlugin<Options> {
export default class<T extends BoldOptions> extends MarkPlugin<T> {
static get pluginName() {
return 'bold';
}

View File

@ -1,7 +1,7 @@
{
"name": "@aomao/plugin-code",
"version": "2.6.23",
"main": "dist/index.ts",
"main": "dist/index.js",
"module": "dist/index.esm.js",
"typings": "dist/index.d.ts",
"files": [

View File

@ -1,11 +1,11 @@
import { NodeInterface, InlinePlugin, PluginOptions } from '@aomao/engine';
import './index.css';
export interface Options extends PluginOptions {
export interface CodeOptions extends PluginOptions {
hotkey?: string | Array<string>;
markdown?: string;
}
export default class extends InlinePlugin<Options> {
export default class<T extends CodeOptions> extends InlinePlugin<T> {
static get pluginName() {
return 'code';
}

View File

@ -6,7 +6,7 @@
"codeblock",
"editor"
],
"main": "dist/index.ts",
"main": "dist/index.js",
"module": "dist/index.esm.js",
"typings": "dist/index.d.ts",
"files": [

View File

@ -8,6 +8,7 @@ import {
isEngine,
isServer,
ToolbarItemOptions,
CardValue,
} from '@aomao/engine';
import CodeBlockEditor from './editor';
import renderSelect from './select';
@ -15,13 +16,13 @@ import modeDatas from './mode';
import { CodeBlockEditorInterface } from './types';
import './index.css';
export type CodeBlockValue = {
export interface CodeBlockValue extends CardValue {
mode?: string;
code?: string;
autoWrap?: boolean;
};
}
class CodeBlcok extends Card<CodeBlockValue> {
class CodeBlcok<V extends CodeBlockValue = CodeBlockValue> extends Card<V> {
mirror?: Editor;
static get cardName() {
return 'codeblock';
@ -73,7 +74,7 @@ class CodeBlcok extends Card<CodeBlockValue> {
this.setValue({
mode,
code: value,
});
} as V);
},
onMouseDown: (event) => {
if (!this.activated)
@ -155,7 +156,7 @@ class CodeBlcok extends Card<CodeBlockValue> {
const autoWrap = !value?.autoWrap;
this.setValue({
autoWrap,
});
} as V);
this.codeEditor?.setAutoWrap(autoWrap);
},
},

View File

@ -15,10 +15,13 @@ import {
READY_CARD_KEY,
decodeCardValue,
} from '@aomao/engine';
import CodeBlockComponent, { CodeBlockEditor } from './component';
import CodeBlockComponent, {
CodeBlockEditor,
CodeBlockValue,
} from './component';
import locales from './locales';
export interface Options extends PluginOptions {
export interface CodeblockOptions extends PluginOptions {
hotkey?: string | Array<string>;
markdown?: boolean;
}
@ -37,7 +40,7 @@ const MODE_ALIAS: { [key: string]: string } = {
'c++': 'cpp',
};
export default class extends Plugin<Options> {
export default class<T extends CodeblockOptions> extends Plugin<T> {
static get pluginName() {
return 'codeblock';
}
@ -62,12 +65,15 @@ export default class extends Plugin<Options> {
execute(mode: string, value: string) {
if (!isEngine(this.editor)) return;
const { card } = this.editor;
const component = card.insert(CodeBlockComponent.cardName, {
const component = card.insert<
CodeBlockValue,
CodeBlockComponent<CodeBlockValue>
>(CodeBlockComponent.cardName, {
mode,
code: value,
});
setTimeout(() => {
(component as CodeBlockComponent).focusEditor();
component.focusEditor();
}, 200);
}
@ -204,7 +210,7 @@ export default class extends Plugin<Options> {
}
let code = new Parser(node, this.editor).toText();
code = unescape(code.replace(/\u200b/g, ''));
this.editor.card.replaceNode(node, 'codeblock', {
this.editor.card.replaceNode<CodeBlockValue>(node, 'codeblock', {
mode: syntax || 'plain',
code,
});
@ -252,10 +258,14 @@ export default class extends Plugin<Options> {
if (code.endsWith('\n')) code = code.substr(0, code.length - 2);
const tempNode = $('<div></div>');
const carNode = card.replaceNode(tempNode, 'codeblock', {
mode,
code,
});
const carNode = card.replaceNode<CodeBlockValue>(
tempNode,
'codeblock',
{
mode,
code,
},
);
tempNode.remove();
return carNode.get<Element>()?.outerHTML;
@ -306,7 +316,9 @@ export default class extends Plugin<Options> {
`[${CARD_KEY}="${CodeBlockComponent.cardName}"],[${READY_CARD_KEY}="${CodeBlockComponent.cardName}]"`,
).each((cardNode) => {
const node = $(cardNode);
const card = this.editor.card.find(node) as CodeBlockComponent;
const card = this.editor.card.find(
node,
) as CodeBlockComponent<CodeBlockValue>;
const value =
card?.getValue() ||
decodeCardValue(node.attributes(CARD_VALUE_KEY));

View File

@ -6,7 +6,7 @@
"codeblock",
"editor"
],
"main": "dist/index.ts",
"main": "dist/index.js",
"module": "dist/index.esm.js",
"typings": "dist/index.d.ts",
"files": [

View File

@ -8,6 +8,7 @@ import {
isEngine,
isServer,
ToolbarItemOptions,
CardValue,
} from '@aomao/engine';
import CodeBlockEditor from './editor';
import renderSelect from './select';
@ -15,13 +16,13 @@ import modeDatas from './mode';
import { CodeBlockEditorInterface } from './types';
import './index.css';
export type CodeBlockValue = {
export interface CodeBlockValue extends CardValue {
mode?: string;
code?: string;
autoWrap?: boolean;
};
}
class CodeBlcok extends Card<CodeBlockValue> {
class CodeBlcok<V extends CodeBlockValue = CodeBlockValue> extends Card<V> {
mirror?: Editor;
static get cardName() {
return 'codeblock';
@ -72,7 +73,7 @@ class CodeBlcok extends Card<CodeBlockValue> {
this.setValue({
mode,
code: value,
});
} as V);
},
onMouseDown: (event) => {
if (!this.activated)
@ -152,7 +153,7 @@ class CodeBlcok extends Card<CodeBlockValue> {
const autoWrap = !value?.autoWrap;
this.setValue({
autoWrap,
});
} as V);
this.codeEditor?.setAutoWrap(autoWrap);
},
},

View File

@ -15,10 +15,13 @@ import {
READY_CARD_KEY,
decodeCardValue,
} from '@aomao/engine';
import CodeBlockComponent, { CodeBlockEditor } from './component';
import CodeBlockComponent, {
CodeBlockEditor,
CodeBlockValue,
} from './component';
import locales from './locales';
export interface Options extends PluginOptions {
export interface CodeBlockOptions extends PluginOptions {
hotkey?: string | Array<string>;
markdown?: boolean;
}
@ -37,7 +40,7 @@ const MODE_ALIAS = {
'c++': 'cpp',
};
export default class extends Plugin<Options> {
export default class<T extends CodeBlockOptions> extends Plugin<T> {
static get pluginName() {
return 'codeblock';
}
@ -62,12 +65,15 @@ export default class extends Plugin<Options> {
execute(mode: string, value: string) {
if (!isEngine(this.editor)) return;
const { card } = this.editor;
const component = card.insert(CodeBlockComponent.cardName, {
const component = card.insert<
CodeBlockValue,
CodeBlockComponent<CodeBlockValue>
>(CodeBlockComponent.cardName, {
mode,
code: value,
});
setTimeout(() => {
(component as CodeBlockComponent).focusEditor();
component.focusEditor();
}, 200);
}
@ -205,7 +211,7 @@ export default class extends Plugin<Options> {
}
let code = new Parser(node, this.editor).toText();
code = unescape(code.replace(/\u200b/g, ''));
this.editor.card.replaceNode(node, 'codeblock', {
this.editor.card.replaceNode<CodeBlockValue>(node, 'codeblock', {
mode: syntax || 'plain',
code,
});
@ -253,10 +259,14 @@ export default class extends Plugin<Options> {
if (code.endsWith('\n')) code = code.substr(0, code.length - 2);
const tempNode = $('<div></div>');
const carNode = card.replaceNode(tempNode, 'codeblock', {
mode,
code,
});
const carNode = card.replaceNode<CodeBlockValue>(
tempNode,
'codeblock',
{
mode,
code,
},
);
tempNode.remove();
return carNode.get<Element>()?.outerHTML;
@ -307,7 +317,9 @@ export default class extends Plugin<Options> {
`[${CARD_KEY}="${CodeBlockComponent.cardName}"],[${READY_CARD_KEY}="${CodeBlockComponent.cardName}"]`,
).each((cardNode) => {
const node = $(cardNode);
const card = this.editor.card.find(node) as CodeBlockComponent;
const card = this.editor.card.find(
node,
) as CodeBlockComponent<CodeBlockValue>;
const value =
card?.getValue() ||
decodeCardValue(node.attributes(CARD_VALUE_KEY));

View File

@ -1,7 +1,7 @@
{
"name": "@aomao/plugin-embed",
"version": "2.6.23",
"main": "dist/index.ts",
"main": "dist/index.js",
"module": "dist/index.esm.js",
"typings": "dist/index.d.ts",
"files": [

View File

@ -23,7 +23,7 @@ export interface EmbedValue extends CardValue {
export type EmbedRenderBeforeEvent = (url: string) => EmbedValue;
class EmbedComponent extends Card<EmbedValue> {
class EmbedComponent<V extends EmbedValue = EmbedValue> extends Card<V> {
renderBefore?: EmbedRenderBeforeEvent;
static get cardName() {
@ -66,7 +66,7 @@ class EmbedComponent extends Card<EmbedValue> {
if (value?.collapsed) return;
this.setValue({
collapsed: true,
});
} as V);
this.render();
super.didRender();
}
@ -76,7 +76,7 @@ class EmbedComponent extends Card<EmbedValue> {
if (!value?.collapsed) return;
this.setValue({
collapsed: false,
});
} as V);
this.render();
super.didRender();
}
@ -145,7 +145,7 @@ class EmbedComponent extends Card<EmbedValue> {
...info,
};
}
this.setValue(value);
this.setValue(value as V);
this.render();
this.#mask?.hide();
this.toolbarModel?.show();

View File

@ -18,11 +18,11 @@ import EmbedComponent, {
} from './component';
import locales from './locales';
export interface Options extends PluginOptions {
export interface EmbedOptions extends PluginOptions {
renderBefore?: EmbedRenderBeforeEvent;
}
class Embed extends Plugin<Options> {
class Embed<T extends EmbedOptions> extends Plugin<T> {
static get pluginName() {
return 'embed';
}
@ -41,7 +41,7 @@ class Embed extends Plugin<Options> {
execute(...args: any): void {
const { renderBefore } = this.options;
const { card } = this.editor;
const cardComponent = card.insert(
const cardComponent = card.insert<EmbedValue>(
EmbedComponent.cardName,
{
url: args[0] || '',
@ -95,12 +95,10 @@ class Embed extends Plugin<Options> {
`[${CARD_KEY}="${EmbedComponent.cardName}"],[${READY_CARD_KEY}="${EmbedComponent.cardName}"]`,
).each((cardNode) => {
const node = $(cardNode);
const card = this.editor.card.find(node) as EmbedComponent;
const card = this.editor.card.find<EmbedValue>(node);
const value =
card?.getValue() ||
(decodeCardValue(
node.attributes(CARD_VALUE_KEY),
) as EmbedValue);
decodeCardValue<EmbedValue>(node.attributes(CARD_VALUE_KEY));
if (value && value.url) {
const iframe = $(
`<iframe frameborder="0" allowfullscreen="true" style="height: ${value?.height}px;width: 100%;margin:0;padding:0;"></iframe>`,

View File

@ -1,7 +1,7 @@
{
"name": "@aomao/plugin-file",
"version": "2.6.23",
"main": "dist/index.ts",
"main": "dist/index.js",
"module": "dist/index.esm.js",
"typings": "dist/index.d.ts",
"files": [

View File

@ -10,10 +10,12 @@ import {
getFileSize,
isEngine,
Tooltip,
SelectStyleType,
CardValue,
} from '@aomao/engine';
import './index.css';
export type FileValue = {
export interface FileValue extends CardValue {
/**
*
*/
@ -48,9 +50,9 @@ export type FileValue = {
*
*/
message?: string;
};
}
export default class FileCard extends Card<FileValue> {
export default class FileCard<V extends FileValue = FileValue> extends Card<V> {
static get cardName() {
return 'file';
}
@ -59,8 +61,8 @@ export default class FileCard extends Card<FileValue> {
return CardType.INLINE;
}
static get selectStyleType(): 'background' {
return 'background';
static get selectStyleType() {
return SelectStyleType.BACKGROUND;
}
static get autoSelected() {
@ -230,7 +232,7 @@ export default class FileCard extends Card<FileValue> {
this.container?.find('.percent').html(`${percent}%`);
this.setValue({
percent,
});
} as V);
}
onActivate(activated: boolean) {

View File

@ -12,12 +12,15 @@ import {
PluginEntry,
READY_CARD_KEY,
SchemaInterface,
PluginOptions,
} from '@aomao/engine';
import FileComponent, { FileValue } from './component';
import FileUploader from './uploader';
import locales from './locales';
export default class extends Plugin {
export interface FileOptions extends PluginOptions {}
export default class<T extends FileOptions> extends Plugin<T> {
static get pluginName() {
return 'file';
}
@ -52,7 +55,7 @@ export default class extends Plugin {
value.url = '';
value.message = url;
}
this.editor.card.insert('file', value) as FileComponent;
this.editor.card.insert<FileValue>('file', value);
}
async waiting(
@ -67,26 +70,23 @@ export default class extends Plugin {
const check = (component: CardInterface) => {
return (
component.root.inEditor() &&
(component.constructor as CardEntry).cardName ===
FileComponent.cardName &&
(component as FileComponent).getValue()?.status === 'uploading'
component.name === FileComponent.cardName &&
(component as FileComponent<FileValue>).getValue()?.status ===
'uploading'
);
};
// 找到不合格的组件
const find = (): CardInterface | undefined => {
const find = () => {
return card.components.find(check);
};
const waitCheck = (component: CardInterface): Promise<void> => {
let time = 60000;
return new Promise((resolve, reject) => {
if (callback) {
const result = callback(
(this.constructor as PluginEntry).pluginName,
component,
);
const result = callback(this.name, component);
if (result === false) {
return reject({
name: (this.constructor as PluginEntry).pluginName,
name: this.name,
card: component,
});
} else if (typeof result === 'number') {
@ -160,7 +160,9 @@ export default class extends Plugin {
`[${CARD_KEY}="${FileComponent.cardName}"],[${READY_CARD_KEY}="${FileComponent.cardName}"`,
).each((cardNode) => {
const node = $(cardNode);
const card = this.editor.card.find(node) as FileComponent;
const card = this.editor.card.find(
node,
) as FileComponent<FileValue>;
const value =
card?.getValue() ||
decodeCardValue(node.attributes(CARD_VALUE_KEY));
@ -182,3 +184,4 @@ export default class extends Plugin {
}
export { FileComponent, FileUploader };
export type { FileValue };

View File

@ -12,9 +12,9 @@ import {
encodeCardValue,
} from '@aomao/engine';
import FileComponent from './component';
import FileComponent, { FileValue } from './component';
export interface Options extends PluginOptions {
export interface FileUploaderOptions extends PluginOptions {
/**
*
*/
@ -75,8 +75,8 @@ export interface Options extends PluginOptions {
};
}
export default class extends Plugin<Options> {
private cardComponents: { [key: string]: FileComponent } = {};
export default class<T extends FileUploaderOptions> extends Plugin<T> {
private cardComponents: { [key: string]: FileComponent<FileValue> } = {};
static get pluginName() {
return 'file-uploader';
@ -171,11 +171,14 @@ export default class extends Plugin<Options> {
!!this.cardComponents[fileInfo.uid]
)
return;
const component = card.insert('file', {
const component = card.insert<
FileValue,
FileComponent<FileValue>
>('file', {
status: 'uploading',
name: fileInfo.name,
size: fileInfo.size,
}) as FileComponent;
});
this.cardComponents[fileInfo.uid] = component;
},
onUploading: (file, { percent }) => {
@ -256,11 +259,15 @@ export default class extends Plugin<Options> {
result = { result: false, data: response.data };
}
if (!result.result) {
card.update(component.id, {
card.update<FileValue>(component.id, {
status: 'error',
message:
result.data ||
this.editor.language.get('file', 'uploadError'),
typeof result.data === 'string'
? result.data
: this.editor.language.get<string>(
'file',
'uploadError',
),
});
} else {
const value: any =
@ -276,11 +283,14 @@ export default class extends Plugin<Options> {
onError: (error, file) => {
const component = this.cardComponents[file.uid || ''];
if (!component) return;
card.update(component.id, {
card.update<FileValue>(component.id, {
status: 'error',
message:
error.message ||
this.editor.language.get('file', 'uploadError'),
this.editor.language.get<string>(
'file',
'uploadError',
),
});
delete this.cardComponents[file.uid || ''];
},

View File

@ -1,7 +1,7 @@
{
"name": "@aomao/plugin-fontcolor",
"version": "2.6.23",
"main": "dist/index.ts",
"main": "dist/index.js",
"module": "dist/index.esm.js",
"typings": "dist/index.d.ts",
"files": [

View File

@ -1,9 +1,9 @@
import { MarkPlugin, PluginOptions } from '@aomao/engine';
export interface Options extends PluginOptions {
export interface FontcolorOptions extends PluginOptions {
hotkey?: { key: string; args: Array<string> };
}
export default class extends MarkPlugin<Options> {
export default class<T extends FontcolorOptions> extends MarkPlugin<T> {
static get pluginName() {
return 'fontcolor';
}

View File

@ -1,7 +1,7 @@
{
"name": "@aomao/plugin-fontfamily",
"version": "2.6.23",
"main": "dist/index.ts",
"main": "dist/index.js",
"module": "dist/index.esm.js",
"typings": "dist/index.d.ts",
"files": [

View File

@ -6,11 +6,11 @@ import {
PluginOptions,
} from '@aomao/engine';
export interface Options extends PluginOptions {
export interface FontfamilyOptions extends PluginOptions {
hotkey?: { key: string; args: Array<string> };
filter?: (fontfamily: string) => string | boolean;
}
export default class extends MarkPlugin<Options> {
export default class<T extends FontfamilyOptions> extends MarkPlugin<T> {
static get pluginName() {
return 'fontfamily';
}

View File

@ -1,7 +1,7 @@
{
"name": "@aomao/plugin-fontsize",
"version": "2.6.23",
"main": "dist/index.ts",
"main": "dist/index.js",
"module": "dist/index.esm.js",
"typings": "dist/index.d.ts",
"files": [

View File

@ -5,13 +5,13 @@ import {
PluginOptions,
} from '@aomao/engine';
export interface Options extends PluginOptions {
export interface FontsizeOptions extends PluginOptions {
hotkey?: { key: string; args: Array<string> };
defaultSize?: string;
filter?: (fontSize: string) => string | boolean;
}
export default class extends MarkPlugin<Options> {
export default class<T extends FontsizeOptions> extends MarkPlugin<T> {
static get pluginName() {
return 'fontsize';
}

View File

@ -1,7 +1,7 @@
{
"name": "@aomao/plugin-heading",
"version": "2.6.23",
"main": "dist/index.ts",
"main": "dist/index.js",
"module": "dist/index.esm.js",
"typings": "dist/index.d.ts",
"files": [

View File

@ -13,7 +13,7 @@ import Outline from './outline';
import type { OutlineData } from './outline';
import './index.css';
export interface Options extends PluginOptions {
export interface HeadingOptions extends PluginOptions {
hotkey?: {
h1?: string;
h2?: string;
@ -28,7 +28,7 @@ export interface Options extends PluginOptions {
enableTypes?: Array<string>;
disableMark?: Array<string>;
}
export default class extends BlockPlugin<Options> {
export default class<T extends HeadingOptions> extends BlockPlugin<T> {
attributes = {
id: '@var0',
};

View File

@ -1,7 +1,7 @@
{
"name": "@aomao/plugin-hr",
"version": "2.6.23",
"main": "dist/index.ts",
"main": "dist/index.js",
"module": "dist/index.esm.js",
"typings": "dist/index.d.ts",
"files": [

View File

@ -4,9 +4,12 @@ import {
CardType,
isEngine,
ToolbarItemOptions,
SelectStyleType,
CardValue,
} from '@aomao/engine';
import './index.css';
class Hr extends Card {
export interface HrValue extends CardValue {}
class Hr<T extends HrValue = HrValue> extends Card<T> {
static get cardName() {
return 'hr';
}
@ -19,8 +22,8 @@ class Hr extends Card {
return false;
}
static get selectStyleType(): 'background' {
return 'background';
static get selectStyleType() {
return SelectStyleType.BACKGROUND;
}
toolbar(): Array<ToolbarItemOptions | CardToolbarItemOptions> {

View File

@ -8,13 +8,13 @@ import {
SchemaInterface,
PluginOptions,
} from '@aomao/engine';
import HrComponent from './component';
import HrComponent, { HrValue } from './component';
export interface Options extends PluginOptions {
export interface HrOptions extends PluginOptions {
hotkey?: string | Array<string>;
markdown?: boolean;
}
export default class extends Plugin<Options> {
export default class<T extends HrOptions> extends Plugin<T> {
static get pluginName() {
return 'hr';
}
@ -143,3 +143,4 @@ export default class extends Plugin<Options> {
}
}
export { HrComponent };
export type { HrValue };

View File

@ -2,7 +2,7 @@
"name": "@aomao/plugin-image",
"version": "2.6.23",
"description": "图片",
"main": "dist/index.ts",
"main": "dist/index.js",
"module": "dist/index.esm.js",
"typings": "dist/index.d.ts",
"files": [

View File

@ -10,6 +10,7 @@ import {
Resizer,
CardType,
} from '@aomao/engine';
import { ImageValue } from '..';
import Pswp from '../pswp';
import './index.css';
@ -405,16 +406,19 @@ class Image {
return image.find('img').length > 0;
})
.forEach((imageNode, index) => {
const card = this.editor.card.find(imageNode);
if (!card) return;
const card = this.editor.card.find<ImageValue>(imageNode);
const value = card?.getValue();
if (!card || !value) return;
const image = card.getCenter().find('img');
const value = card.getValue() || {};
const imageWidth = parseInt(image.css('width'));
const imageHeight = parseInt(image.css('height'));
const naturalWidth =
value.size['naturalWidth'] || imageWidth * winPixelRatio;
const naturalHeight =
value.size['naturalHeight'] || imageHeight * winPixelRatio;
const size = value.size;
const naturalWidth = size
? size.naturalWidth
: imageWidth * winPixelRatio;
const naturalHeight = size
? size.naturalHeight
: imageHeight * winPixelRatio;
let src = value['src'];
const { onBeforeRender } = this.options;
if (onBeforeRender) src = onBeforeRender('done', src);

View File

@ -2,6 +2,7 @@ import {
Card,
CardToolbarItemOptions,
CardType,
CardValue,
isEngine,
isMobile,
NodeInterface,
@ -9,7 +10,7 @@ import {
} from '@aomao/engine';
import Image, { Size } from './image';
export type ImageValue = {
export interface ImageValue extends CardValue {
/**
*
*/
@ -64,9 +65,9 @@ export type ImageValue = {
*/
naturalHeight: number;
};
};
}
class ImageComponent extends Card<ImageValue> {
class ImageComponent<T extends ImageValue = ImageValue> extends Card<T> {
private image?: Image;
private widthInput?: NodeInterface;
private heightInput?: NodeInterface;
@ -92,11 +93,11 @@ class ImageComponent extends Card<ImageValue> {
this.image?.setProgressPercent(percent);
this.setValue({
percent,
});
} as T);
}
setSize(size: Size) {
this.setValue({ size } as ImageValue);
this.setValue({ size } as T);
if (this.widthInput) {
this.widthInput.get<HTMLInputElement>()!.value =
size.width.toString();

View File

@ -10,15 +10,18 @@ import {
NodeInterface,
Plugin,
PluginEntry,
PluginOptions,
READY_CARD_KEY,
} from '@aomao/engine';
import ImageComponent, { ImageValue } from './component';
import ImageUploader from './uploader';
import locales from './locales';
export default class extends Plugin<{
export interface ImageOptions extends PluginOptions {
onBeforeRender?: (status: 'uploading' | 'done', src: string) => string;
}> {
}
export default class<T extends ImageOptions> extends Plugin<T> {
static get pluginName() {
return 'image';
}
@ -57,9 +60,9 @@ export default class extends Plugin<{
const check = (component: CardInterface) => {
return (
component.root.inEditor() &&
(component.constructor as CardEntry).cardName ===
ImageComponent.cardName &&
(component as ImageComponent).getValue()?.status === 'uploading'
component.name === ImageComponent.cardName &&
(component as ImageComponent<ImageValue>).getValue()?.status ===
'uploading'
);
};
// 找到不合格的组件
@ -116,7 +119,9 @@ export default class extends Plugin<{
`[${CARD_KEY}="${ImageComponent.cardName}"],[${READY_CARD_KEY}="${ImageComponent.cardName}"]`,
).each((cardNode) => {
const node = $(cardNode);
const card = this.editor.card.find(node) as ImageComponent;
const card = this.editor.card.find(
node,
) as ImageComponent<ImageValue>;
const value =
card?.getValue() ||
decodeCardValue(node.attributes(CARD_VALUE_KEY));
@ -132,8 +137,8 @@ export default class extends Plugin<{
img.attributes('src', src);
img.css('visibility', 'visible');
const size = value.size;
if (size.width) img.css('width', `${size.width}px`);
if (size.height) img.css('height', `${size.height}px`);
if (size?.width) img.css('width', `${size.width}px`);
if (size?.height) img.css('height', `${size.height}px`);
img.removeAttributes('class');
img.attributes('data-type', type);
if (img.length > 0) {
@ -151,3 +156,4 @@ export default class extends Plugin<{
}
export { ImageComponent, ImageUploader };
export type { ImageValue };

View File

@ -18,7 +18,7 @@ import {
} from '@aomao/engine';
import ImageComponent, { ImageValue } from './component';
export interface Options extends PluginOptions {
export interface ImageUploaderOptions extends PluginOptions {
/**
*
*/
@ -122,8 +122,8 @@ export interface Options extends PluginOptions {
isRemote?: (src: string) => boolean;
}
export default class extends Plugin<Options> {
private cardComponents: { [key: string]: ImageComponent } = {};
export default class<T extends ImageUploaderOptions> extends Plugin<T> {
private cardComponents: { [key: string]: ImageComponent<ImageValue> } = {};
private loadCounts: { [key: string]: number } = {};
static get pluginName() {
@ -304,7 +304,10 @@ export default class extends Plugin<Options> {
)
: src;
const insertCard = (value: Partial<ImageValue>) => {
const component = card.insert(
const component = card.insert<
ImageValue,
ImageComponent<ImageValue>
>(
'image',
{
...value,
@ -312,7 +315,7 @@ export default class extends Plugin<Options> {
//fileInfo.src, 再协作中如果大图片使用base64加载图片预览会造成很大资源浪费
},
base64String,
) as ImageComponent;
);
this.cardComponents[fileInfo.uid] = component;
};
return new Promise<void>((resolve) => {
@ -355,11 +358,11 @@ export default class extends Plugin<Options> {
? { result: true, data: src }
: { result: false };
if (!result.result) {
card.update(component.id, {
card.update<ImageValue>(component.id, {
status: 'error',
message:
result.data ||
this.editor.language.get(
this.editor.language.get<string>(
'image',
'uploadError',
),
@ -379,11 +382,14 @@ export default class extends Plugin<Options> {
onError: (error, file) => {
const component = this.cardComponents[file.uid || ''];
if (!component) return;
card.update(component.id, {
card.update<ImageValue>(component.id, {
status: 'error',
message:
error.message ||
this.editor.language.get('image', 'uploadError'),
this.editor.language.get<string>(
'image',
'uploadError',
),
});
delete this.cardComponents[file.uid || ''];
},
@ -564,11 +570,14 @@ export default class extends Plugin<Options> {
? { result: true, data: src }
: { result: false };
if (!result.result) {
this.editor.card.update(component.id, {
this.editor.card.update<ImageValue>(component.id, {
status: 'error',
message:
result.data ||
this.editor.language.get('image', 'uploadError'),
this.editor.language.get<string>(
'image',
'uploadError',
),
});
} else {
src = result.data;
@ -583,18 +592,21 @@ export default class extends Plugin<Options> {
}
},
error: (error) => {
this.editor.card.update(component.id, {
this.editor.card.update<ImageValue>(component.id, {
status: 'error',
message:
error.message ||
this.editor.language.get('image', 'uploadError'),
this.editor.language.get<string>(
'image',
'uploadError',
),
});
},
});
}
insertRemote(src: string, alt?: string) {
const value = {
const value: ImageValue = {
src,
alt,
status: 'uploading',
@ -602,10 +614,10 @@ export default class extends Plugin<Options> {
const { isRemote } = this.options;
//上传第三方图片
if (isRemote && isRemote(src)) {
const component = this.editor.card.insert(
'image',
value,
) as ImageComponent;
const component = this.editor.card.insert<
ImageValue,
ImageComponent<ImageValue>
>('image', value);
this.uploadAddress(src, component);
return;
}
@ -724,7 +736,7 @@ export default class extends Plugin<Options> {
const src = match[4] || match[8];
const link = isLink ? match[5] : '';
const cardNode = card.replaceNode($(regNode), 'image', {
const cardNode = card.replaceNode<ImageValue>($(regNode), 'image', {
src,
status:
(isRemote && isRemote(src)) || /^data:image\//i.test(src)

View File

@ -1,7 +1,7 @@
{
"name": "@aomao/plugin-indent",
"version": "2.6.23",
"main": "dist/index.ts",
"main": "dist/index.js",
"module": "dist/index.esm.js",
"typings": "dist/index.d.ts",
"files": [

View File

@ -12,7 +12,7 @@ import {
ConversionToValue,
} from '@aomao/engine';
export interface Options extends PluginOptions {
export interface IndentOptions extends PluginOptions {
hotkey?: {
in?: string;
out?: string;
@ -20,9 +20,9 @@ export interface Options extends PluginOptions {
maxPadding?: number;
}
const TEXT_INENT_KEY = 'text-indent';
export const TEXT_INENT_KEY = 'text-indent';
export default class extends Plugin<Options> {
export default class<T extends IndentOptions> extends Plugin<T> {
static get pluginName() {
return 'indent';
}

View File

@ -1,7 +1,7 @@
{
"name": "@aomao/plugin-italic",
"version": "2.6.23",
"main": "dist/index.ts",
"main": "dist/index.js",
"module": "dist/index.esm.js",
"typings": "dist/index.d.ts",
"files": [

View File

@ -1,10 +1,10 @@
import { MarkPlugin, PluginOptions } from '@aomao/engine';
export interface Options extends PluginOptions {
export interface ItalicOptions extends PluginOptions {
hotkey?: string | Array<string>;
markdown?: string;
}
export default class extends MarkPlugin<Options> {
export default class<T extends ItalicOptions> extends MarkPlugin<T> {
static get pluginName() {
return 'italic';
}

View File

@ -1,7 +1,7 @@
{
"name": "@aomao/plugin-line-height",
"version": "2.6.23",
"main": "dist/index.ts",
"main": "dist/index.js",
"module": "dist/index.esm.js",
"typings": "dist/index.d.ts",
"files": [

View File

@ -6,12 +6,12 @@ import {
PluginOptions,
} from '@aomao/engine';
export interface Options extends PluginOptions {
export interface LineHeightOptions extends PluginOptions {
hotkey?: string;
filter?: (lineHeight: string) => string | boolean;
}
export default class extends Plugin<Options> {
export default class<T extends LineHeightOptions> extends Plugin<T> {
static get pluginName() {
return 'line-height';
}

View File

@ -2,7 +2,7 @@
"name": "@aomao/plugin-link-vue",
"version": "2.6.23",
"description": "> TODO: description",
"main": "dist/index.ts",
"main": "dist/index.js",
"module": "dist/index.esm.js",
"typings": "dist/index.d.ts",
"files": [

View File

@ -11,11 +11,11 @@ import locales from './locales';
import './index.css';
export interface Options extends PluginOptions {
export interface LinkOptions extends PluginOptions {
hotkey?: string | Array<string>;
markdown?: string;
}
export default class extends InlinePlugin<Options> {
export default class<T extends LinkOptions> extends InlinePlugin<T> {
private toolbar?: Toolbar;
static get pluginName() {

Some files were not shown because too many files have changed in this diff Show More