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 "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", "key": "1",
"name": "User1", "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", "key": "2",
"name": "User2", "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", "key": "3",
"name": "User3", "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", "key": "4",
"name": "Test1", "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", "key": "5",
"name": "Test2", "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", "key": "6",
"name": "Test3", "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'); const path = require('path');
module.exports = (appInfo, appConfig = {}) => { module.exports = (appInfo, appConfig = {}) => {
const assetsDir = (appConfig.assets && appConfig.assets.assetsDir) || '../'; const assetsDir = (appConfig.assets && appConfig.assets.assetsDir) || '../';
@ -16,20 +15,6 @@ module.exports = (appInfo, appConfig = {}) => {
fileSize: '20mb', fileSize: '20mb',
whitelist: files.split(',').map((name) => name.trim()), 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 = { config.static = {
prefix: '/', prefix: '/',
dir: path.join(appInfo.baseDir, 'app/public'), 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'; 'use strict';
module.exports = { module.exports = {
assets: {
enable: true,
package: 'egg-view-assets',
},
nunjucks: { nunjucks: {
enable: true, enable: true,
package: 'egg-view-nunjucks', 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", "stop": "egg-scripts stop --title=egg-aomao-ssr",
"dev": "egg-bin dev --port=7001 --sticky", "dev": "egg-bin dev --port=7001 --sticky",
"debug": "cross-env RM_TMPDIR=none COMPRESS=none egg-bin debug", "debug": "cross-env RM_TMPDIR=none COMPRESS=none egg-bin debug",
"start-doc": "cd ../ && yarn start",
"test": "egg-bin test", "test": "egg-bin test",
"cov": "egg-bin cov", "cov": "egg-bin cov",
"lint": "eslint .", "lint": "eslint .",
@ -20,10 +19,10 @@
}, },
"dependencies": { "dependencies": {
"cross-env": "^5.2.0", "cross-env": "^5.2.0",
"egg": "^2.29.3", "egg": "^2.33.1",
"egg-cors": "^2.2.3", "egg-cors": "^2.2.3",
"egg-scripts": "^2.13.0", "egg-scripts": "^2.13.0",
"egg-view-assets": "^1.7.0", "egg-view-assets": "^1.8.0",
"egg-view-nunjucks": "^2.3.0", "egg-view-nunjucks": "^2.3.0",
"fluent-ffmpeg": "^2.1.2", "fluent-ffmpeg": "^2.1.2",
"jsdom": "^16.4.0", "jsdom": "^16.4.0",
@ -34,12 +33,12 @@
}, },
"devDependencies": { "devDependencies": {
"@types/qs": "^6.5.3", "@types/qs": "^6.5.3",
"autod": "^3.0.1", "autod": "^3.1.2",
"autod-egg": "^1.0.0", "autod-egg": "^1.0.0",
"babel-plugin-dva-hmr": "^0.4.0", "babel-plugin-dva-hmr": "^0.4.0",
"babel-plugin-import": "^1.12.0", "babel-plugin-import": "^1.12.0",
"egg-bin": "^4.9.0", "egg-bin": "^4.16.3",
"egg-mock": "^3.20.1", "egg-mock": "^4.2.0",
"eslint": "^5.8.0", "eslint": "^5.8.0",
"eslint-config-egg": "^7.1.0", "eslint-config-egg": "^7.1.0",
"redbox-react": "^1.5.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'; export default (opts: {
lang?: string;
function getMenus(opts: { lang?: string; base: '/docs' | '/plugin' | '/api' }) { base: '/docs' | '/plugin' | '/api';
}) => {
const menus = { const menus = {
'/docs': [ '/docs': [
{ {
@ -235,141 +236,4 @@ function getMenus(opts: { lang?: string; base: '/docs' | '/plugin' | '/api' }) {
title: menu[`title_${opts.lang}`] || menu.title, 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 ```ts
/** /**
* 获取可编辑区域选中的所有节点 * 获取卡片区域选中的所有节点
*/ */
getSelectionNodes?(): Array<NodeInterface> getSelectionNodes?(): Array<NodeInterface>
``` ```

View File

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

View File

@ -1,11 +1,6 @@
import { isServer } from '@aomao/engine'; import { isServer } from '@aomao/engine';
export const IS_DEV = process.env.NODE_ENV !== 'production'; 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 = ( export const lang = (
!isServer ? window.location.href.indexOf('zh-CN') > 0 : false !isServer ? window.location.href.indexOf('zh-CN') > 0 : false

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,3 +1,4 @@
import { isMarkPlugin } from './plugin';
import { ChangeInterface } from './types'; import { ChangeInterface } from './types';
import { CommandInterface } from './types/command'; import { CommandInterface } from './types/command';
import { EditorInterface } from './types/engine'; import { EditorInterface } from './types/engine';
@ -30,8 +31,15 @@ class Command implements CommandInterface {
) )
return false; 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; return false;
}
// TODO:查询当前所处位置的插件 // TODO:查询当前所处位置的插件
return true; return true;
} }

View File

@ -661,13 +661,22 @@
vertical-align: bottom; 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; min-width: 1px;
text-align: left; text-align: left;
-webkit-user-select: text; -webkit-user-select: text;
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 { .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; display: block;
} }

View File

@ -376,6 +376,7 @@ class Inline implements InlineModelInterface {
change.delete(safeRange); change.delete(safeRange);
} }
mark.split(safeRange); mark.split(safeRange);
this.split(safeRange);
// 插入新 Inline // 插入新 Inline
node.insert(inline, safeRange)?.select(inline).collapse(false); node.insert(inline, safeRange)?.select(inline).collapse(false);
@ -515,12 +516,36 @@ class Inline implements InlineModelInterface {
rightNodes[0].remove(); rightNodes[0].remove();
rightNodes.splice(0, 1); 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) => { parent.traverse((child) => {
if (node.isInline(child)) { if (node.isInline(child)) {
this.repairCursor(child); this.repairCursor(child);
} }
}); });
} }
range.enlargeToElementNode();
return keelpNode; return keelpNode;
} }
/** /**

View File

@ -741,6 +741,130 @@ class Mark implements MarkModelInterface {
if (!range) change.apply(safeRange); 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标签 * mark标签
* @param mark mark标签 * @param mark mark标签
@ -875,160 +999,30 @@ class Mark implements MarkModelInterface {
started = false; started = false;
return false; return false;
} }
const childIsMark = nodeApi.isMark(child);
const result = this.wrapByNode(child, mark, plugin);
// 要包裹的节点是mark // 要包裹的节点是mark
if (nodeApi.isMark(child)) { if (
if (!nodeApi.isEmpty(child)) { result &&
//找到最底层mark标签添加包裹<strong><span style="font-size:16px">abc</span></strong> ,在 span 节点中的text再添加包裹不在strong外添加包裹 typeof result !== 'boolean' &&
let targetNode = child; childIsMark
let targetChildrens = targetNode.children(); ) {
const curPlugin = if (
this.findPlugin(targetNode); !isEditable &&
while ( selection?.focus &&
nodeApi.isMark(targetNode) && result
targetChildrens.length === 1 && .find(
plugin && `[data-element="${selection.focus.attributes(
curPlugin && DATA_ELEMENT,
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
) )
return; .equal(selection.focus)
if (nodeApi.isMark(children)) { ) {
const childPlugin = started = false;
this.findPlugin(children); return false;
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();
} }
} }
if (typeof result !== 'undefined') return true;
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);
}
} }
} else { } else {
started = true; started = true;
@ -1110,7 +1104,7 @@ class Mark implements MarkModelInterface {
*/ */
merge(range?: RangeInterface): void { merge(range?: RangeInterface): void {
if (!isEngine(this.editor)) return; if (!isEngine(this.editor)) return;
const { change, node } = this.editor; const { change } = this.editor;
const safeRange = range || change.range.toTrusty(); const safeRange = range || change.range.toTrusty();
const marks = this.findMarks(safeRange); const marks = this.findMarks(safeRange);
if (marks.length === 0) { if (marks.length === 0) {
@ -1122,6 +1116,73 @@ class Mark implements MarkModelInterface {
safeRange.handleBr(); safeRange.handleBr();
if (!range) change.apply(safeRange); 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包裹 * mark包裹
* @param range * @param range
@ -1208,6 +1269,23 @@ class Mark implements MarkModelInterface {
safeRange.isPointInRange(child, 0)) safeRange.isPointInRange(child, 0))
) { ) {
markNodes.push(child); 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, true,
); );
}); });
// 清除 Mark this.unwrapByNodes(markNodes, removeMark);
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');
}
});
selection?.move(); selection?.move();
if (isEditable) { if (isEditable) {
const markNodes: NodeInterface[] = []; const markNodes: NodeInterface[] = [];
@ -1468,6 +1501,11 @@ class Mark implements MarkModelInterface {
!node.attributes(CARD_ELEMENT_KEY) !node.attributes(CARD_ELEMENT_KEY)
) { ) {
nodes.push(node); 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(); const parent = node.parent();
if (!parent) break; if (!parent) break;
@ -1514,6 +1552,16 @@ class Mark implements MarkModelInterface {
!child.attributes(CARD_ELEMENT_KEY) !child.attributes(CARD_ELEMENT_KEY)
) { ) {
addNode(nodes, child); 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 { } else {

View File

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

View File

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

View File

@ -3,6 +3,7 @@ import {
ElementPluginInterface, ElementPluginInterface,
NodeInterface, NodeInterface,
ConversionData, ConversionData,
PluginInterface,
} from '../types'; } from '../types';
import { import {
SchemaAttributes, SchemaAttributes,
@ -16,10 +17,11 @@ import { $ } from '../node';
import PluginEntry from './base'; import PluginEntry from './base';
import { isNode } from '../node/utils'; import { isNode } from '../node/utils';
abstract class ElementPluginEntry<T extends PluginOptions = {}> abstract class ElementPluginEntry<T extends PluginOptions>
extends PluginEntry<T> extends PluginEntry<T>
implements ElementPluginInterface implements ElementPluginInterface
{ {
readonly kind: string = 'element';
/** /**
* *
*/ */
@ -242,6 +244,23 @@ abstract class ElementPluginEntry<T extends PluginOptions = {}>
* b > strong * b > strong
*/ */
conversion?(): ConversionData; 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 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 { EditorInterface } from '../types/engine';
import { import {
ElementPluginInterface,
PluginEntry, PluginEntry,
PluginInterface, PluginInterface,
PluginModelInterface, PluginModelInterface,
PluginOptions, PluginOptions,
} from '../types/plugin'; } from '../types/plugin';
import Plugin from './base'; import Plugin from './base';
import ElementPlugin from './element'; import ElementPlugin, { isElementPlugin } from './element';
import BlockPlugin, { isBlockPlugin } from './block'; import BlockPlugin, { isBlockPlugin } from './block';
import InlinePlugin, { isInlinePlugin } from './inline'; import InlinePlugin, { isInlinePlugin } from './inline';
import ListPlugin from './list'; import ListPlugin from './list';
import MarkPlugin, { isMarkPlugin } from './mark'; import MarkPlugin, { isMarkPlugin } from './mark';
import { isEngine } from '../utils'; import { isEngine } from '../utils';
import { BlockInterface, InlineInterface, MarkInterface } from 'src';
class PluginModel implements PluginModelInterface { class PluginModel implements PluginModelInterface {
protected data: { [k: string]: PluginEntry } = {}; protected data: Record<string, PluginEntry> = {};
components: { [k: string]: PluginInterface } = {}; components: Record<string, PluginInterface<PluginOptions>> = {};
protected editor: EditorInterface; protected editor: EditorInterface;
constructor(editor: EditorInterface) { constructor(editor: EditorInterface) {
this.editor = editor; 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( each(
callback: ( callback: (
name: string, name: string,

View File

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

View File

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

View File

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

View File

@ -56,6 +56,27 @@ export interface MarkModelInterface {
* @param both mark标签两侧节点 * @param both mark标签两侧节点
*/ */
wrap(mark: NodeInterface | Node | string, range?: RangeInterface): void; 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包裹 * mark包裹
* @param range * @param range

View File

@ -1,3 +1,4 @@
import { BlockInterface, InlineInterface, MarkInterface } from '.';
import { CardInterface } from './card'; import { CardInterface } from './card';
import { ConversionData } from './conversion'; import { ConversionData } from './conversion';
import { EditorInterface } from './engine'; import { EditorInterface } from './engine';
@ -18,12 +19,16 @@ export type PluginOptions = {
export interface PluginEntry { export interface PluginEntry {
prototype: PluginInterface; prototype: PluginInterface;
new (editor: EditorInterface, options: PluginOptions): PluginInterface; new (
editor: EditorInterface,
options: PluginOptions,
): PluginInterface<PluginOptions>;
readonly pluginName: string; readonly pluginName: string;
} }
export interface PluginInterface<T extends PluginOptions = {}> { export interface PluginInterface<T extends PluginOptions = {}> {
readonly kind: string; readonly kind: string;
readonly name: string;
/** /**
* *
**/ **/
@ -146,6 +151,12 @@ export interface ElementPluginInterface extends PluginInterface {
* b > strong * b > strong
*/ */
conversion?(): ConversionData; conversion?(): ConversionData;
/**
*
* @param args
* @returns
*/
createElement(...args: any): NodeInterface;
} }
export interface PluginModelInterface { export interface PluginModelInterface {
@ -175,4 +186,13 @@ export interface PluginModelInterface {
index?: number, index?: number,
) => boolean | void, ) => boolean | void,
): 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组件值编码 * Card组件值编码
* @param value * @param value
*/ */
export const encodeCardValue = (value: any): string => { export const encodeCardValue = <T = Record<string, any>>(value: T): string => {
let str = '';
try { try {
value = encodeURIComponent(JSON.stringify(value || '')); str = encodeURIComponent(JSON.stringify(value || ''));
} catch (e) { } catch (e) {}
value = '';
}
return 'data:'.concat(value); return 'data:'.concat(str);
}; };
/** /**
* Card组件值解码 * Card组件值解码
* @param value * @param value
*/ */
export const decodeCardValue = (value: string): any => { export const decodeCardValue = <T = Record<string, any>>(value: string): T => {
try { try {
value = value.substr(5); value = value.substr(5);
return JSON.parse(decodeURIComponent(value)); return JSON.parse(decodeURIComponent(value));
} catch (e) { } catch (e) {
return {}; return {} as T;
} }
}; };

View File

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

View File

@ -6,6 +6,7 @@ import {
isHotkey, isHotkey,
CardType, CardType,
isServer, isServer,
CardValue,
} from '@aomao/engine'; } from '@aomao/engine';
import { import {
CollapseGroupProps, CollapseGroupProps,
@ -16,9 +17,13 @@ import { getToolbarDefaultConfig } from '../../config';
import CollapseComponent, { CollapseComponentInterface } from './collapse'; import CollapseComponent, { CollapseComponentInterface } from './collapse';
import './index.css'; 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 keyword?: NodeInterface;
private placeholder?: NodeInterface; private placeholder?: NodeInterface;
private component?: CollapseComponentInterface; private component?: CollapseComponentInterface;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -12,7 +12,7 @@ import {
ConversionToValue, ConversionToValue,
} from '@aomao/engine'; } from '@aomao/engine';
export interface Options extends PluginOptions { export interface IndentOptions extends PluginOptions {
hotkey?: { hotkey?: {
in?: string; in?: string;
out?: string; out?: string;
@ -20,9 +20,9 @@ export interface Options extends PluginOptions {
maxPadding?: number; 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() { static get pluginName() {
return 'indent'; return 'indent';
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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