mirror of
https://github.com/TriliumNext/Notes.git
synced 2025-07-27 10:02:59 +08:00
Add '_regroup/ckeditor5-mermaid/' from commit 'c15257da7e57b6303fda9744ee4153d1c5311d6f'
git-subtree-dir: _regroup/ckeditor5-mermaid git-subtree-mainline: 90c0f417131d254e86a4a4ab391122cad0930db7 git-subtree-split: c15257da7e57b6303fda9744ee4153d1c5311d6f
This commit is contained in:
commit
178903f6b2
19
_regroup/ckeditor5-mermaid/.editorconfig
Normal file
19
_regroup/ckeditor5-mermaid/.editorconfig
Normal file
@ -0,0 +1,19 @@
|
||||
# Configurations to normalize the IDE behavior.
|
||||
# http://editorconfig.org/
|
||||
|
||||
root = true
|
||||
|
||||
[*]
|
||||
indent_style = tab
|
||||
tab_width = 4
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
|
||||
[*.{js,jsx,ts}]
|
||||
quote_type = single
|
||||
|
||||
[package.json]
|
||||
indent_style = space
|
||||
tab_width = 2
|
41
_regroup/ckeditor5-mermaid/.eslintrc.cjs
Normal file
41
_regroup/ckeditor5-mermaid/.eslintrc.cjs
Normal file
@ -0,0 +1,41 @@
|
||||
/* eslint-env node */
|
||||
|
||||
'use strict';
|
||||
|
||||
module.exports = {
|
||||
extends: 'ckeditor5',
|
||||
root: true,
|
||||
ignorePatterns: [
|
||||
// Ignore the entire `build/` (the DLL build).
|
||||
'build/**',
|
||||
// Ignore the entire `dist/`.
|
||||
'dist/**'
|
||||
],
|
||||
rules: {
|
||||
// This rule disallows importing core DLL packages directly. Imports should be done using the `ckeditor5` package.
|
||||
// Also, importing non-DLL packages is not allowed. If the package requires other features to work, they should be
|
||||
// specified as soft-requirements.
|
||||
// Read more: https://ckeditor.com/docs/ckeditor5/latest/builds/guides/migration/migration-to-26.html#soft-requirements.
|
||||
'ckeditor5-rules/ckeditor-imports': 'error',
|
||||
// This rule disallows importing from any path other than the package main entrypoint.
|
||||
'ckeditor5-rules/allow-imports-only-from-main-package-entry-point': 'error',
|
||||
// As required by the ECMAScript (ESM) standard, all imports must include a file extension.
|
||||
// If the import does not include it, this rule will try to automatically detect the correct file extension.
|
||||
'ckeditor5-rules/require-file-extensions-in-imports': [
|
||||
'error',
|
||||
{
|
||||
extensions: [ '.ts', '.js', '.json' ]
|
||||
}
|
||||
]
|
||||
},
|
||||
overrides: [
|
||||
{
|
||||
files: [ 'tests/**/*.js', 'sample/**/*.js' ],
|
||||
rules: {
|
||||
// To write complex tests, you may need to import files that are not exported in DLL files by default.
|
||||
// Hence, imports CKEditor 5 packages in test files are not checked.
|
||||
'ckeditor5-rules/ckeditor-imports': 'off'
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
18
_regroup/ckeditor5-mermaid/.gitattributes
vendored
Normal file
18
_regroup/ckeditor5-mermaid/.gitattributes
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
* text=auto
|
||||
|
||||
*.htaccess eol=lf
|
||||
*.cgi eol=lf
|
||||
*.sh eol=lf
|
||||
|
||||
*.css text
|
||||
*.htm text
|
||||
*.html text
|
||||
*.js text
|
||||
*.json text
|
||||
*.php text
|
||||
*.txt text
|
||||
*.md text
|
||||
|
||||
*.png -text
|
||||
*.gif -text
|
||||
*.jpg -text
|
8
_regroup/ckeditor5-mermaid/.gitignore
vendored
Normal file
8
_regroup/ckeditor5-mermaid/.gitignore
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
.nyc_output/
|
||||
coverage/
|
||||
node_modules/
|
||||
yarn.lock
|
||||
tmp/
|
||||
build/
|
||||
dist
|
||||
yarn.error
|
6
_regroup/ckeditor5-mermaid/.stylelintrc
Normal file
6
_regroup/ckeditor5-mermaid/.stylelintrc
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"extends": "stylelint-config-ckeditor5",
|
||||
"ignoreFiles": [
|
||||
"dist/**/*.css"
|
||||
]
|
||||
}
|
22
_regroup/ckeditor5-mermaid/LICENSE.md
Normal file
22
_regroup/ckeditor5-mermaid/LICENSE.md
Normal file
@ -0,0 +1,22 @@
|
||||
Software License Agreement
|
||||
==========================
|
||||
|
||||
**CKEditor 5 mermaid feature** (https://ckeditor.com/ckeditor-5/)<br>
|
||||
Copyright (c) 2003-2022, [CKSource](http://cksource.com) Holding sp. z o.o. All rights reserved.
|
||||
|
||||
Licensed under the terms of [GNU General Public License Version 2 or later](http://www.gnu.org/licenses/gpl.html).
|
||||
|
||||
Sources of Intellectual Property Included in CKEditor 5 mermaid feature
|
||||
---------------------------------------------------------------------
|
||||
|
||||
Where not otherwise indicated, all CKEditor 5 mermaid feature content is authored by CKSource engineers and consists of CKSource-owned intellectual property.
|
||||
|
||||
The following libraries are included in CKEditor 5 mermaid feature under the [MIT license](https://opensource.org/licenses/MIT):
|
||||
|
||||
* Lo-Dash - Copyright (c) JS Foundation and other contributors https://js.foundation/. Based on Underscore.js, copyright Jeremy Ashkenas.
|
||||
* mermaid - Copyright (c) 2014 - 2021 Knut Sveidqvist. (MIT License)
|
||||
|
||||
Trademarks
|
||||
----------
|
||||
|
||||
**CKEditor** is a trademark of [CKSource](http://cksource.com) Holding sp. z o.o. All other brand and product names are trademarks, registered trademarks or service marks of their respective holders.
|
24
_regroup/ckeditor5-mermaid/README.md
Normal file
24
_regroup/ckeditor5-mermaid/README.md
Normal file
@ -0,0 +1,24 @@
|
||||
CKEditor 5 Mermaid feature
|
||||
=================================
|
||||
|
||||
Warning: This is an experimental plugin that comes with no support, use it at your own risk.
|
||||
|
||||
This package contains a Mermaid feature for CKEditor 5.
|
||||
|
||||
## CKEditor 5 Mermaid - Running manual test
|
||||
|
||||
You can test all of the features of the `ckeditor5-mermaid` plugin with the manual test sample.
|
||||
|
||||
After installing dependencies it's enough to execute the following script at the root of your local clone of the project:
|
||||
|
||||
```
|
||||
yarn start
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
See [LICENSE.md](LICENSE.md) file.
|
||||
|
||||
### Trademarks
|
||||
|
||||
**CKEditor** is a trademark of [CKSource](https://cksource.com) Holding sp. z o.o. All other brand and product names are trademarks, registered trademarks or service marks of their respective holders.
|
77
_regroup/ckeditor5-mermaid/package.json
Normal file
77
_regroup/ckeditor5-mermaid/package.json
Normal file
@ -0,0 +1,77 @@
|
||||
{
|
||||
"name": "@ckeditor/ckeditor5-mermaid",
|
||||
"version": "0.0.4",
|
||||
"description": "Mermaid widget for CKEditor 5.",
|
||||
"private": true,
|
||||
"keywords": [
|
||||
"ckeditor",
|
||||
"ckeditor5",
|
||||
"ckeditor 5",
|
||||
"ckeditor5-feature",
|
||||
"ckeditor5-plugin",
|
||||
"ckeditor5-mermaid"
|
||||
],
|
||||
"type": "module",
|
||||
"main": "src/index.js",
|
||||
"license": "SEE LICENSE IN LICENSE.md",
|
||||
"author": "CKSource (https://cksource.com/)",
|
||||
"homepage": "https://github.com/ckeditor/ckeditor5-mermaid",
|
||||
"bugs": "https://github.com/ckeditor/ckeditor5-mermaid/issues",
|
||||
"engines": {
|
||||
"node": ">=18.0.0",
|
||||
"npm": ">=5.7.1"
|
||||
},
|
||||
"files": [
|
||||
"lang",
|
||||
"src/**/*.js",
|
||||
"src/**/*.css",
|
||||
"theme",
|
||||
"build",
|
||||
"ckeditor5-metadata.json"
|
||||
],
|
||||
"dependencies": {
|
||||
"ckeditor5": "43.2.0",
|
||||
"lodash-es": "^4.17.15"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@ckeditor/ckeditor5-dev-build-tools": "^42.0.0",
|
||||
"@ckeditor/ckeditor5-inspector": "^4.0.0",
|
||||
"@ckeditor/ckeditor5-package-tools": "^2.1.0",
|
||||
"ckeditor5": "latest",
|
||||
"eslint": "^7.32.0",
|
||||
"eslint-config-ckeditor5": "^6.0.0",
|
||||
"http-server": "^14.1.1",
|
||||
"husky": "^4.2.5",
|
||||
"lint-staged": "^12.0.0",
|
||||
"stylelint": "^13.13.1",
|
||||
"stylelint-config-ckeditor5": ">=6.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"ckeditor5": ">=43.0.0 || ^0.0.0-nightly"
|
||||
},
|
||||
"scripts": {
|
||||
"dll:build": "ckeditor5-package-tools dll:build",
|
||||
"dll:serve": "http-server ./ -o sample/dll.html",
|
||||
"lint": "eslint --quiet --ext .ts src/",
|
||||
"lint:fix": "eslint --quiet --fix --ext .ts src/",
|
||||
"stylelint": "stylelint --quiet --allow-empty-input 'theme/**/*.css'",
|
||||
"test": "ckeditor5-package-tools test",
|
||||
"prepare": "yarn run dll:build",
|
||||
"prepublishOnly": "ckeditor5-package-tools export-package-as-javascript",
|
||||
"postpublish": "ckeditor5-package-tools export-package-as-typescript",
|
||||
"start": "ckeditor5-package-tools start"
|
||||
},
|
||||
"lint-staged": {
|
||||
"**/*.js": [
|
||||
"eslint --quiet"
|
||||
],
|
||||
"**/*.css": [
|
||||
"stylelint --quiet --allow-empty-input"
|
||||
]
|
||||
},
|
||||
"husky": {
|
||||
"hooks": {
|
||||
"pre-commit": "lint-staged"
|
||||
}
|
||||
}
|
||||
}
|
54
_regroup/ckeditor5-mermaid/sample/ckeditor.js
vendored
Normal file
54
_regroup/ckeditor5-mermaid/sample/ckeditor.js
vendored
Normal file
@ -0,0 +1,54 @@
|
||||
/**
|
||||
* @license Copyright (c) 2003-2023, CKSource Holding sp. z o.o. All rights reserved.
|
||||
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
|
||||
*/
|
||||
|
||||
/* globals console, window, document */
|
||||
|
||||
import { ClassicEditor } from '@ckeditor/ckeditor5-editor-classic';
|
||||
import { Typing } from '@ckeditor/ckeditor5-typing';
|
||||
import { Paragraph } from '@ckeditor/ckeditor5-paragraph';
|
||||
import { Undo } from '@ckeditor/ckeditor5-undo';
|
||||
import { Enter } from '@ckeditor/ckeditor5-enter';
|
||||
import { Clipboard } from '@ckeditor/ckeditor5-clipboard';
|
||||
import { Link } from '@ckeditor/ckeditor5-link';
|
||||
import { Bold, Italic } from '@ckeditor/ckeditor5-basic-styles';
|
||||
import { CodeBlock } from '@ckeditor/ckeditor5-code-block';
|
||||
|
||||
import CKEditorInspector from '@ckeditor/ckeditor5-inspector';
|
||||
|
||||
import Mermaid from '../src/mermaid.js';
|
||||
|
||||
ClassicEditor
|
||||
.create( document.querySelector( '#editor' ), {
|
||||
plugins: [
|
||||
Typing,
|
||||
Paragraph,
|
||||
Undo,
|
||||
Enter,
|
||||
Clipboard,
|
||||
Link,
|
||||
Bold,
|
||||
Italic,
|
||||
CodeBlock,
|
||||
Mermaid
|
||||
],
|
||||
toolbar: [ 'bold', 'italic', 'link', 'undo', 'redo', 'codeBlock', 'mermaid' ],
|
||||
codeBlock: {
|
||||
languages: [
|
||||
{ language: 'plaintext', label: 'Plain text', class: '' },
|
||||
{ language: 'javascript', label: 'JavaScript' },
|
||||
{ language: 'python', label: 'Python' },
|
||||
{ language: 'mermaid', label: 'Mermaid' }
|
||||
]
|
||||
}
|
||||
|
||||
} )
|
||||
.then( editor => {
|
||||
window.editor = editor;
|
||||
CKEditorInspector.attach( editor );
|
||||
window.console.log( 'CKEditor 5 is ready.', editor );
|
||||
} )
|
||||
.catch( err => {
|
||||
console.error( err.stack );
|
||||
} );
|
119
_regroup/ckeditor5-mermaid/sample/index.html
Normal file
119
_regroup/ckeditor5-mermaid/sample/index.html
Normal file
@ -0,0 +1,119 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>CKEditor 5 Mermaid widget – Development Sample</title>
|
||||
<style>
|
||||
body {
|
||||
max-width: 800px;
|
||||
margin: 20px auto;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<h1>CKEditor 5 Mermaid widget – Development Sample</h1>
|
||||
|
||||
<div id="editor">
|
||||
</div>
|
||||
|
||||
<!-- DLL builds are served from the `node_modules/` directory -->
|
||||
<script src="../node_modules/ckeditor5/build/ckeditor5-dll.js"></script>
|
||||
<script src="../node_modules/@ckeditor/ckeditor5-editor-classic/build/editor-classic.js"></script>
|
||||
<script src="../node_modules/@ckeditor/ckeditor5-code-block/build/code-block.js"></script>
|
||||
<script src="../node_modules/@ckeditor/ckeditor5-essentials/build/essentials.js"></script>
|
||||
<script src="../node_modules/@ckeditor/ckeditor5-basic-styles/build/basic-styles.js"></script>
|
||||
<script src="../node_modules/@ckeditor/ckeditor5-heading/build/heading.js"></script>
|
||||
<script src="../node_modules/@ckeditor/ckeditor5-autoformat/build/autoformat.js"></script>
|
||||
<script src="../node_modules/@ckeditor/ckeditor5-block-quote/build/block-quote.js"></script>
|
||||
<script src="../node_modules/@ckeditor/ckeditor5-image/build/image.js"></script>
|
||||
<script src="../node_modules/@ckeditor/ckeditor5-link/build/link.js"></script>
|
||||
<script src="../node_modules/@ckeditor/ckeditor5-indent/build/indent.js"></script>
|
||||
<script src="../node_modules/@ckeditor/ckeditor5-media-embed/build/media-embed.js"></script>
|
||||
<script src="../node_modules/@ckeditor/ckeditor5-list/build/list.js"></script>
|
||||
<script src="../node_modules/@ckeditor/ckeditor5-table/build/table.js"></script>
|
||||
|
||||
<!-- The "@ckeditor/ckeditor5-mermaid" package DLL build is served from the `build/` directory -->
|
||||
<script src="../build/mermaid.js"></script>
|
||||
|
||||
<script>
|
||||
console.log( 'Objects exported by the DLL build:', CKEditor5[ 'mermaid' ] );
|
||||
|
||||
CKEditor5.editorClassic.ClassicEditor
|
||||
.create( document.querySelector( '#editor' ), {
|
||||
plugins: [
|
||||
CKEditor5[ 'mermaid' ].Mermaid,
|
||||
CKEditor5.essentials.Essentials,
|
||||
CKEditor5.autoformat.Autoformat,
|
||||
CKEditor5.blockQuote.BlockQuote,
|
||||
CKEditor5.basicStyles.Bold,
|
||||
CKEditor5.heading.Heading,
|
||||
CKEditor5.image.Image,
|
||||
CKEditor5.image.ImageCaption,
|
||||
CKEditor5.image.ImageStyle,
|
||||
CKEditor5.image.ImageToolbar,
|
||||
CKEditor5.image.ImageUpload,
|
||||
CKEditor5.indent.Indent,
|
||||
CKEditor5.basicStyles.Italic,
|
||||
CKEditor5.link.Link,
|
||||
CKEditor5.list.List,
|
||||
CKEditor5.mediaEmbed.MediaEmbed,
|
||||
CKEditor5.paragraph.Paragraph,
|
||||
CKEditor5.table.Table,
|
||||
CKEditor5.table.TableToolbar,
|
||||
CKEditor5.codeBlock.CodeBlock,
|
||||
CKEditor5.basicStyles.Code,
|
||||
CKEditor5.upload.Base64UploadAdapter
|
||||
],
|
||||
toolbar: [
|
||||
'mermaid',
|
||||
'|',
|
||||
'undo',
|
||||
'redo',
|
||||
'|',
|
||||
'heading',
|
||||
'|',
|
||||
'bold',
|
||||
'italic',
|
||||
'link',
|
||||
'code',
|
||||
'bulletedList',
|
||||
'numberedList',
|
||||
'|',
|
||||
'outdent',
|
||||
'indent',
|
||||
'|',
|
||||
'uploadImage',
|
||||
'blockQuote',
|
||||
'insertTable',
|
||||
'mediaEmbed',
|
||||
'codeBlock'
|
||||
],
|
||||
image: {
|
||||
toolbar: [
|
||||
'imageStyle:inline',
|
||||
'imageStyle:block',
|
||||
'imageStyle:side',
|
||||
'|',
|
||||
'imageTextAlternative'
|
||||
]
|
||||
},
|
||||
table: {
|
||||
contentToolbar: [
|
||||
'tableColumn',
|
||||
'tableRow',
|
||||
'mergeTableCells'
|
||||
]
|
||||
}
|
||||
} )
|
||||
.then( editor => {
|
||||
window.editor = editor;
|
||||
} )
|
||||
.catch( error => {
|
||||
console.error( 'There was a problem initializing the editor.', error );
|
||||
} );
|
||||
</script>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
61
_regroup/ckeditor5-mermaid/scripts/build-dist.mjs
Normal file
61
_regroup/ckeditor5-mermaid/scripts/build-dist.mjs
Normal file
@ -0,0 +1,61 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* @license Copyright (c) 2020-2024, CKSource Holding sp. z o.o. All rights reserved.
|
||||
* For licensing, see LICENSE.md.
|
||||
*/
|
||||
|
||||
/* eslint-env node */
|
||||
|
||||
import { createRequire } from 'module';
|
||||
import upath from 'upath';
|
||||
import chalk from 'chalk';
|
||||
import { build } from '@ckeditor/ckeditor5-dev-build-tools';
|
||||
|
||||
function dist( path ) {
|
||||
return upath.join( 'dist', path );
|
||||
}
|
||||
|
||||
( async () => {
|
||||
/**
|
||||
* Step 1
|
||||
*/
|
||||
console.log( chalk.cyan( '1/2: Generating NPM build...' ) );
|
||||
|
||||
const require = createRequire( import.meta.url );
|
||||
const pkg = require( upath.resolve( process.cwd(), './package.json' ) );
|
||||
|
||||
await build( {
|
||||
input: 'src/index.js',
|
||||
output: dist( './index.js' ),
|
||||
external: [
|
||||
'ckeditor5',
|
||||
'ckeditor5-premium-features',
|
||||
...Object.keys( {
|
||||
...pkg.dependencies,
|
||||
...pkg.peerDependencies
|
||||
} )
|
||||
],
|
||||
clean: true,
|
||||
sourceMap: true,
|
||||
translations: '**/*.po'
|
||||
} );
|
||||
|
||||
/**
|
||||
* Step 2
|
||||
*/
|
||||
console.log( chalk.cyan( '2/2: Generating browser build...' ) );
|
||||
|
||||
await build( {
|
||||
input: 'src/index.js',
|
||||
output: dist( 'browser/index.js' ),
|
||||
sourceMap: true,
|
||||
minify: true,
|
||||
browser: true,
|
||||
name: '@ckeditor/ckeditor5-mermaid',
|
||||
external: [
|
||||
'ckeditor5',
|
||||
'ckeditor5-premium-features'
|
||||
]
|
||||
} );
|
||||
} )();
|
@ -0,0 +1,52 @@
|
||||
/**
|
||||
* @module mermaid/insertmermaidcommand
|
||||
*/
|
||||
|
||||
import { Command } from 'ckeditor5/src/core.js';
|
||||
|
||||
const MOCK_MERMAID_MARKUP = `flowchart TB
|
||||
A --> B
|
||||
B --> C`;
|
||||
|
||||
/**
|
||||
* The insert mermaid command.
|
||||
*
|
||||
* Allows to insert mermaid.
|
||||
*
|
||||
* @extends module:core/command~Command
|
||||
*/
|
||||
export default class InsertMermaidCommand extends Command {
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
refresh() {
|
||||
const documentSelection = this.editor.model.document.selection;
|
||||
const selectedElement = documentSelection.getSelectedElement();
|
||||
|
||||
if ( selectedElement && selectedElement.name === 'mermaid' ) {
|
||||
this.isEnabled = false;
|
||||
} else {
|
||||
this.isEnabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
execute() {
|
||||
const editor = this.editor;
|
||||
const model = editor.model;
|
||||
let mermaidItem;
|
||||
|
||||
model.change( writer => {
|
||||
mermaidItem = writer.createElement( 'mermaid', {
|
||||
displayMode: 'split',
|
||||
source: MOCK_MERMAID_MARKUP
|
||||
} );
|
||||
|
||||
model.insertContent( mermaidItem );
|
||||
} );
|
||||
|
||||
return mermaidItem;
|
||||
}
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
/**
|
||||
* @module mermaid/mermaidpreviewcommand
|
||||
*/
|
||||
|
||||
import { Command } from 'ckeditor5/src/core.js';
|
||||
|
||||
import { checkIsOn } from '../utils.js';
|
||||
|
||||
/**
|
||||
* The mermaid preview command.
|
||||
*
|
||||
* Allows to switch to a preview mode.
|
||||
*
|
||||
* @extends module:core/command~Command
|
||||
*/
|
||||
export default class MermaidPreviewCommand extends Command {
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
refresh() {
|
||||
const editor = this.editor;
|
||||
const documentSelection = editor.model.document.selection;
|
||||
const selectedElement = documentSelection.getSelectedElement();
|
||||
const isSelectedElementMermaid = selectedElement && selectedElement.name === 'mermaid';
|
||||
|
||||
if ( isSelectedElementMermaid || documentSelection.getLastPosition().findAncestor( 'mermaid' ) ) {
|
||||
this.isEnabled = !!selectedElement;
|
||||
} else {
|
||||
this.isEnabled = false;
|
||||
}
|
||||
|
||||
this.value = checkIsOn( editor, 'preview' );
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
execute() {
|
||||
const editor = this.editor;
|
||||
const model = editor.model;
|
||||
const documentSelection = this.editor.model.document.selection;
|
||||
const mermaidItem = documentSelection.getSelectedElement() || documentSelection.getLastPosition().parent;
|
||||
|
||||
model.change( writer => {
|
||||
if ( mermaidItem.getAttribute( 'displayMode' ) !== 'preview' ) {
|
||||
writer.setAttribute( 'displayMode', 'preview', mermaidItem );
|
||||
}
|
||||
} );
|
||||
}
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
/**
|
||||
* @module mermaid/mermaidsourceviewcommand
|
||||
*/
|
||||
|
||||
import { Command } from 'ckeditor5/src/core.js';
|
||||
|
||||
import { checkIsOn } from '../utils.js';
|
||||
|
||||
/**
|
||||
* The mermaid source view command.
|
||||
*
|
||||
* Allows to switch to a source view mode.
|
||||
*
|
||||
* @extends module:core/command~Command
|
||||
*/
|
||||
export default class MermaidSourceViewCommand extends Command {
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
refresh() {
|
||||
const editor = this.editor;
|
||||
const documentSelection = editor.model.document.selection;
|
||||
const selectedElement = documentSelection.getSelectedElement();
|
||||
const isSelectedElementMermaid = selectedElement && selectedElement.name === 'mermaid';
|
||||
|
||||
if ( isSelectedElementMermaid || documentSelection.getLastPosition().findAncestor( 'mermaid' ) ) {
|
||||
this.isEnabled = !!selectedElement;
|
||||
} else {
|
||||
this.isEnabled = false;
|
||||
}
|
||||
|
||||
this.value = checkIsOn( editor, 'source' );
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
execute() {
|
||||
const editor = this.editor;
|
||||
const model = editor.model;
|
||||
const documentSelection = this.editor.model.document.selection;
|
||||
const mermaidItem = documentSelection.getSelectedElement() || documentSelection.getLastPosition().parent;
|
||||
|
||||
model.change( writer => {
|
||||
if ( mermaidItem.getAttribute( 'displayMode' ) !== 'source' ) {
|
||||
writer.setAttribute( 'displayMode', 'source', mermaidItem );
|
||||
}
|
||||
} );
|
||||
}
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
/**
|
||||
* @module mermaid/mermaidsplitviewcommand
|
||||
*/
|
||||
|
||||
import { Command } from 'ckeditor5/src/core.js';
|
||||
|
||||
import { checkIsOn } from '../utils.js';
|
||||
|
||||
/**
|
||||
* The mermaid split view command.
|
||||
*
|
||||
* Allows to switch to a split view mode.
|
||||
*
|
||||
* @extends module:core/command~Command
|
||||
*/
|
||||
export default class MermaidSplitViewCommand extends Command {
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
refresh() {
|
||||
const editor = this.editor;
|
||||
const documentSelection = editor.model.document.selection;
|
||||
const selectedElement = documentSelection.getSelectedElement();
|
||||
const isSelectedElementMermaid = selectedElement && selectedElement.name === 'mermaid';
|
||||
|
||||
if ( isSelectedElementMermaid || documentSelection.getLastPosition().findAncestor( 'mermaid' ) ) {
|
||||
this.isEnabled = !!selectedElement;
|
||||
} else {
|
||||
this.isEnabled = false;
|
||||
}
|
||||
|
||||
this.value = checkIsOn( editor, 'split' );
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
execute() {
|
||||
const editor = this.editor;
|
||||
const model = editor.model;
|
||||
const documentSelection = this.editor.model.document.selection;
|
||||
const mermaidItem = documentSelection.getSelectedElement() || documentSelection.getLastPosition().parent;
|
||||
|
||||
model.change( writer => {
|
||||
if ( mermaidItem.getAttribute( 'displayMode' ) !== 'split' ) {
|
||||
writer.setAttribute( 'displayMode', 'split', mermaidItem );
|
||||
}
|
||||
} );
|
||||
}
|
||||
}
|
19
_regroup/ckeditor5-mermaid/src/index.js
Normal file
19
_regroup/ckeditor5-mermaid/src/index.js
Normal file
@ -0,0 +1,19 @@
|
||||
/**
|
||||
* @module mermaid
|
||||
*/
|
||||
|
||||
import infoIcon from './../theme/icons/info.svg';
|
||||
import insertMermaidIcon from './../theme/icons/insert.svg';
|
||||
import previewModeIcon from './../theme/icons/preview-mode.svg';
|
||||
import splitModeIcon from './../theme/icons/split-mode.svg';
|
||||
import sourceModeIcon from './../theme/icons/source-mode.svg';
|
||||
|
||||
export { default as Mermaid } from './mermaid.js';
|
||||
|
||||
export const icons = {
|
||||
infoIcon,
|
||||
insertMermaidIcon,
|
||||
previewModeIcon,
|
||||
splitModeIcon,
|
||||
sourceModeIcon
|
||||
};
|
27
_regroup/ckeditor5-mermaid/src/mermaid.js
Normal file
27
_regroup/ckeditor5-mermaid/src/mermaid.js
Normal file
@ -0,0 +1,27 @@
|
||||
/**
|
||||
* @module mermaid/mermaid
|
||||
*/
|
||||
|
||||
import { Plugin } from 'ckeditor5/src/core.js';
|
||||
|
||||
import MermaidEditing from './mermaidediting.js';
|
||||
import MermaidToolbar from './mermaidtoolbar.js';
|
||||
import MermaidUI from './mermaidui.js';
|
||||
|
||||
import '../theme/mermaid.css';
|
||||
|
||||
export default class Mermaid extends Plugin {
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
static get requires() {
|
||||
return [ MermaidEditing, MermaidToolbar, MermaidUI ];
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
static get pluginName() {
|
||||
return 'Mermaid';
|
||||
}
|
||||
}
|
295
_regroup/ckeditor5-mermaid/src/mermaidediting.js
Normal file
295
_regroup/ckeditor5-mermaid/src/mermaidediting.js
Normal file
@ -0,0 +1,295 @@
|
||||
/**
|
||||
* @module mermaid/mermaidediting
|
||||
*/
|
||||
|
||||
import { Plugin } from 'ckeditor5/src/core.js';
|
||||
import { toWidget } from 'ckeditor5/src/widget.js';
|
||||
|
||||
import { debounce } from 'lodash-es';
|
||||
|
||||
import MermaidPreviewCommand from './commands/mermaidPreviewCommand.js';
|
||||
import MermaidSourceViewCommand from './commands/mermaidSourceViewCommand.js';
|
||||
import MermaidSplitViewCommand from './commands/mermaidSplitViewCommand.js';
|
||||
import InsertMermaidCommand from './commands/insertMermaidCommand.js';
|
||||
|
||||
// Time in milliseconds.
|
||||
const DEBOUNCE_TIME = 300;
|
||||
|
||||
/* global window */
|
||||
|
||||
export default class MermaidEditing extends Plugin {
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
static get pluginName() {
|
||||
return 'MermaidEditing';
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
init() {
|
||||
this._registerCommands();
|
||||
this._defineConverters();
|
||||
this._config = this.editor.config.get("mermaid");
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
afterInit() {
|
||||
this.editor.model.schema.register( 'mermaid', {
|
||||
allowAttributes: [ 'displayMode', 'source' ],
|
||||
allowWhere: '$block',
|
||||
isObject: true
|
||||
} );
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
_registerCommands() {
|
||||
const editor = this.editor;
|
||||
|
||||
editor.commands.add( 'mermaidPreviewCommand', new MermaidPreviewCommand( editor ) );
|
||||
editor.commands.add( 'mermaidSplitViewCommand', new MermaidSplitViewCommand( editor ) );
|
||||
editor.commands.add( 'mermaidSourceViewCommand', new MermaidSourceViewCommand( editor ) );
|
||||
editor.commands.add( 'insertMermaidCommand', new InsertMermaidCommand( editor ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds converters.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_defineConverters() {
|
||||
const editor = this.editor;
|
||||
|
||||
editor.data.downcastDispatcher.on( 'insert:mermaid', this._mermaidDataDowncast.bind( this ) );
|
||||
editor.editing.downcastDispatcher.on( 'insert:mermaid', this._mermaidDowncast.bind( this ) );
|
||||
editor.editing.downcastDispatcher.on( 'attribute:source:mermaid', this._sourceAttributeDowncast.bind( this ) );
|
||||
|
||||
editor.data.upcastDispatcher.on( 'element:code', this._mermaidUpcast.bind( this ), { priority: 'high' } );
|
||||
|
||||
editor.conversion.for( 'editingDowncast' ).attributeToAttribute( {
|
||||
model: {
|
||||
name: 'mermaid',
|
||||
key: 'displayMode'
|
||||
},
|
||||
view: modelAttributeValue => ( {
|
||||
key: 'class',
|
||||
value: 'ck-mermaid__' + modelAttributeValue + '-mode'
|
||||
} )
|
||||
} );
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @private
|
||||
* @param {*} evt
|
||||
* @param {*} data
|
||||
* @param {*} conversionApi
|
||||
*/
|
||||
_mermaidDataDowncast( evt, data, conversionApi ) {
|
||||
const model = this.editor.model;
|
||||
const { writer, mapper } = conversionApi;
|
||||
|
||||
if ( !conversionApi.consumable.consume( data.item, 'insert' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
const targetViewPosition = mapper.toViewPosition( model.createPositionBefore( data.item ) );
|
||||
// For downcast we're using only language-mermaid class. We don't set class to `mermaid language-mermaid` as
|
||||
// multiple markdown converters that we have seen are using only `language-mermaid` class and not `mermaid` alone.
|
||||
const code = writer.createContainerElement( 'code', {
|
||||
class: 'language-mermaid'
|
||||
} );
|
||||
const pre = writer.createContainerElement( 'pre', {
|
||||
spellcheck: 'false'
|
||||
} );
|
||||
const sourceTextNode = writer.createText( data.item.getAttribute( 'source' ) );
|
||||
|
||||
writer.insert( model.createPositionAt( code, 'end' ), sourceTextNode );
|
||||
writer.insert( model.createPositionAt( pre, 'end' ), code );
|
||||
writer.insert( targetViewPosition, pre );
|
||||
mapper.bindElements( data.item, code );
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @private
|
||||
* @param {*} evt
|
||||
* @param {*} data
|
||||
* @param {*} conversionApi
|
||||
*/
|
||||
_mermaidDowncast( evt, data, conversionApi ) {
|
||||
const { writer, mapper, consumable } = conversionApi;
|
||||
const { editor } = this;
|
||||
const { model, t } = editor;
|
||||
const that = this;
|
||||
|
||||
if ( !consumable.consume( data.item, 'insert' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
const targetViewPosition = mapper.toViewPosition( model.createPositionBefore( data.item ) );
|
||||
|
||||
const wrapperAttributes = {
|
||||
class: [ 'ck-mermaid__wrapper' ]
|
||||
};
|
||||
const textareaAttributes = {
|
||||
class: [ 'ck-mermaid__editing-view' ],
|
||||
placeholder: t( 'Insert mermaid source code' ),
|
||||
'data-cke-ignore-events': true
|
||||
};
|
||||
|
||||
const wrapper = writer.createContainerElement( 'div', wrapperAttributes );
|
||||
const editingContainer = writer.createUIElement( 'textarea', textareaAttributes, createEditingTextarea );
|
||||
const previewContainer = writer.createUIElement( 'div', { class: [ 'ck-mermaid__preview' ] }, createMermaidPreview );
|
||||
|
||||
writer.insert( writer.createPositionAt( wrapper, 'start' ), previewContainer );
|
||||
writer.insert( writer.createPositionAt( wrapper, 'start' ), editingContainer );
|
||||
|
||||
writer.insert( targetViewPosition, wrapper );
|
||||
|
||||
mapper.bindElements( data.item, wrapper );
|
||||
|
||||
return toWidget( wrapper, writer, {
|
||||
widgetLabel: t( 'Mermaid widget' ),
|
||||
hasSelectionHandle: true
|
||||
} );
|
||||
|
||||
function createEditingTextarea( domDocument ) {
|
||||
const domElement = this.toDomElement( domDocument );
|
||||
|
||||
domElement.value = data.item.getAttribute( 'source' );
|
||||
|
||||
const debouncedListener = debounce( event => {
|
||||
editor.model.change( writer => {
|
||||
writer.setAttribute( 'source', event.target.value, data.item );
|
||||
} );
|
||||
}, DEBOUNCE_TIME );
|
||||
|
||||
domElement.addEventListener( 'input', debouncedListener );
|
||||
|
||||
/* Workaround for internal #1544 */
|
||||
domElement.addEventListener( 'focus', () => {
|
||||
const model = editor.model;
|
||||
const selectedElement = model.document.selection.getSelectedElement();
|
||||
|
||||
// Move the selection onto the mermaid widget if it's currently not selected.
|
||||
if ( selectedElement !== data.item ) {
|
||||
model.change( writer => writer.setSelection( data.item, 'on' ) );
|
||||
}
|
||||
}, true );
|
||||
|
||||
return domElement;
|
||||
}
|
||||
|
||||
function createMermaidPreview( domDocument ) {
|
||||
// Taking the text from the wrapper container element for now
|
||||
const mermaidSource = data.item.getAttribute( 'source' );
|
||||
const domElement = this.toDomElement( domDocument );
|
||||
|
||||
domElement.innerHTML = mermaidSource;
|
||||
|
||||
window.setTimeout( () => {
|
||||
// @todo: by the looks of it the domElement needs to be hooked to tree in order to allow for rendering.
|
||||
that._renderMermaid( domElement );
|
||||
}, 100 );
|
||||
|
||||
return domElement;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {*} evt
|
||||
* @param {*} data
|
||||
* @param {*} conversionApi
|
||||
* @returns
|
||||
*/
|
||||
_sourceAttributeDowncast( evt, data, conversionApi ) {
|
||||
// @todo: test whether the attribute was consumed.
|
||||
const newSource = data.attributeNewValue;
|
||||
const domConverter = this.editor.editing.view.domConverter;
|
||||
|
||||
if ( newSource ) {
|
||||
const mermaidView = conversionApi.mapper.toViewElement( data.item );
|
||||
|
||||
for ( const child of mermaidView.getChildren() ) {
|
||||
if ( child.name === 'textarea' && child.hasClass( 'ck-mermaid__editing-view' ) ) {
|
||||
const domEditingTextarea = domConverter.viewToDom( child, window.document );
|
||||
|
||||
if ( domEditingTextarea.value != newSource ) {
|
||||
domEditingTextarea.value = newSource;
|
||||
}
|
||||
} else if ( child.name === 'div' && child.hasClass( 'ck-mermaid__preview' ) ) {
|
||||
// @todo: we could optimize this and not refresh mermaid if widget is in source mode.
|
||||
const domPreviewWrapper = domConverter.viewToDom( child, window.document );
|
||||
|
||||
if ( domPreviewWrapper ) {
|
||||
domPreviewWrapper.innerHTML = newSource;
|
||||
domPreviewWrapper.removeAttribute( 'data-processed' );
|
||||
|
||||
this._renderMermaid( domPreviewWrapper );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @private
|
||||
* @param {*} evt
|
||||
* @param {*} data
|
||||
* @param {*} conversionApi
|
||||
*/
|
||||
_mermaidUpcast( evt, data, conversionApi ) {
|
||||
const viewCodeElement = data.viewItem;
|
||||
const hasPreElementParent = !viewCodeElement.parent || !viewCodeElement.parent.is( 'element', 'pre' );
|
||||
const hasCodeAncestors = data.modelCursor.findAncestor( 'code' );
|
||||
const { consumable, writer } = conversionApi;
|
||||
|
||||
if ( !viewCodeElement.hasClass( 'language-mermaid' ) || hasPreElementParent || hasCodeAncestors ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( !consumable.test( viewCodeElement, { name: true } ) ) {
|
||||
return;
|
||||
}
|
||||
const mermaidSource = Array.from( viewCodeElement.getChildren() )
|
||||
.filter( item => item.is( '$text' ) )
|
||||
.map( item => item.data )
|
||||
.join( '' );
|
||||
|
||||
const mermaidElement = writer.createElement( 'mermaid', {
|
||||
source: mermaidSource,
|
||||
displayMode: 'split'
|
||||
} );
|
||||
|
||||
// Let's try to insert mermaid element.
|
||||
if ( !conversionApi.safeInsert( mermaidElement, data.modelCursor ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
consumable.consume( viewCodeElement, { name: true } );
|
||||
|
||||
conversionApi.updateConversionResult( mermaidElement, data );
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders Mermaid in a given `domElement`. Expect this domElement to have mermaid
|
||||
* source set as text content.
|
||||
*
|
||||
* @param {HTMLElement} domElement
|
||||
*/
|
||||
async _renderMermaid( domElement ) {
|
||||
if (!window.mermaid && typeof this._config?.lazyLoad === "function") {
|
||||
this.mermaid = await this._config.lazyLoad();
|
||||
}
|
||||
|
||||
this.mermaid.init( this._config.config, domElement );
|
||||
}
|
||||
}
|
51
_regroup/ckeditor5-mermaid/src/mermaidtoolbar.js
Normal file
51
_regroup/ckeditor5-mermaid/src/mermaidtoolbar.js
Normal file
@ -0,0 +1,51 @@
|
||||
/**
|
||||
* @module mermaid/mermaidtoolbar
|
||||
*/
|
||||
|
||||
import { Plugin } from 'ckeditor5/src/core.js';
|
||||
import { WidgetToolbarRepository } from 'ckeditor5/src/widget.js';
|
||||
|
||||
export default class MermaidToolbar extends Plugin {
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
static get requires() {
|
||||
return [ WidgetToolbarRepository ];
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
static get pluginName() {
|
||||
return 'MermaidToolbar';
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
afterInit() {
|
||||
const editor = this.editor;
|
||||
const t = editor.t;
|
||||
|
||||
const widgetToolbarRepository = editor.plugins.get( WidgetToolbarRepository );
|
||||
const mermaidToolbarItems = [ 'mermaidSourceView', 'mermaidSplitView', 'mermaidPreview', '|', 'mermaidInfo' ];
|
||||
|
||||
if ( mermaidToolbarItems ) {
|
||||
widgetToolbarRepository.register( 'mermaidToolbar', {
|
||||
ariaLabel: t( 'Mermaid toolbar' ),
|
||||
items: mermaidToolbarItems,
|
||||
getRelatedElement: selection => getSelectedElement( selection )
|
||||
} );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getSelectedElement( selection ) {
|
||||
const viewElement = selection.getSelectedElement();
|
||||
|
||||
if ( viewElement && viewElement.hasClass( 'ck-mermaid__wrapper' ) ) {
|
||||
return viewElement;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
150
_regroup/ckeditor5-mermaid/src/mermaidui.js
Normal file
150
_regroup/ckeditor5-mermaid/src/mermaidui.js
Normal file
@ -0,0 +1,150 @@
|
||||
/**
|
||||
* @module mermaid/mermaidui
|
||||
*/
|
||||
|
||||
import { Plugin } from 'ckeditor5/src/core.js';
|
||||
import { ButtonView } from 'ckeditor5/src/ui.js';
|
||||
|
||||
import insertMermaidIcon from '../theme/icons/insert.svg';
|
||||
import previewModeIcon from '../theme/icons/preview-mode.svg';
|
||||
import splitModeIcon from '../theme/icons/split-mode.svg';
|
||||
import sourceModeIcon from '../theme/icons/source-mode.svg';
|
||||
import infoIcon from '../theme/icons/info.svg';
|
||||
|
||||
/* global window, document */
|
||||
|
||||
export default class MermaidUI extends Plugin {
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
static get pluginName() {
|
||||
return 'MermaidUI';
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
init() {
|
||||
this._addButtons();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds all mermaid-related buttons.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_addButtons() {
|
||||
const editor = this.editor;
|
||||
|
||||
this._addInsertMermaidButton();
|
||||
this._addMermaidInfoButton();
|
||||
this._createToolbarButton( editor, 'mermaidPreview', 'Preview', previewModeIcon );
|
||||
this._createToolbarButton( editor, 'mermaidSourceView', 'Source view', sourceModeIcon );
|
||||
this._createToolbarButton( editor, 'mermaidSplitView', 'Split view', splitModeIcon );
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the button for inserting mermaid.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_addInsertMermaidButton() {
|
||||
const editor = this.editor;
|
||||
const t = editor.t;
|
||||
const view = editor.editing.view;
|
||||
|
||||
editor.ui.componentFactory.add( 'mermaid', locale => {
|
||||
const buttonView = new ButtonView( locale );
|
||||
const command = editor.commands.get( 'insertMermaidCommand' );
|
||||
|
||||
buttonView.set( {
|
||||
label: t( 'Insert Mermaid diagram' ),
|
||||
icon: insertMermaidIcon,
|
||||
tooltip: true
|
||||
} );
|
||||
|
||||
buttonView.bind( 'isOn', 'isEnabled' ).to( command, 'value', 'isEnabled' );
|
||||
|
||||
// Execute the command when the button is clicked.
|
||||
command.listenTo( buttonView, 'execute', () => {
|
||||
const mermaidItem = editor.execute( 'insertMermaidCommand' );
|
||||
const mermaidItemViewElement = editor.editing.mapper.toViewElement( mermaidItem );
|
||||
|
||||
view.scrollToTheSelection();
|
||||
view.focus();
|
||||
|
||||
if ( mermaidItemViewElement ) {
|
||||
const mermaidItemDomElement = view.domConverter.viewToDom( mermaidItemViewElement, document );
|
||||
|
||||
if ( mermaidItemDomElement ) {
|
||||
mermaidItemDomElement.querySelector( '.ck-mermaid__editing-view' ).focus();
|
||||
}
|
||||
}
|
||||
} );
|
||||
|
||||
return buttonView;
|
||||
} );
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the button linking to the mermaid guide.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_addMermaidInfoButton() {
|
||||
const editor = this.editor;
|
||||
const t = editor.t;
|
||||
|
||||
editor.ui.componentFactory.add( 'mermaidInfo', locale => {
|
||||
const buttonView = new ButtonView( locale );
|
||||
const link = 'https://ckeditor.com/blog/basic-overview-of-creating-flowcharts-using-mermaid/';
|
||||
|
||||
buttonView.set( {
|
||||
label: t( 'Read more about Mermaid diagram syntax' ),
|
||||
icon: infoIcon,
|
||||
tooltip: true
|
||||
} );
|
||||
|
||||
buttonView.on( 'execute', () => {
|
||||
window.open( link, '_blank', 'noopener' );
|
||||
} );
|
||||
|
||||
return buttonView;
|
||||
} );
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the mermaid balloon toolbar button.
|
||||
*
|
||||
* @private
|
||||
* @param {module:core/editor/editor~Editor} editor
|
||||
* @param {String} name Name of the button.
|
||||
* @param {String} label Label for the button.
|
||||
* @param {String} icon The button icon.
|
||||
*/
|
||||
_createToolbarButton( editor, name, label, icon ) {
|
||||
const t = editor.t;
|
||||
|
||||
editor.ui.componentFactory.add( name, locale => {
|
||||
const buttonView = new ButtonView( locale );
|
||||
const command = editor.commands.get( `${ name }Command` );
|
||||
|
||||
buttonView.set( {
|
||||
label: t( label ),
|
||||
icon,
|
||||
tooltip: true
|
||||
} );
|
||||
|
||||
buttonView.bind( 'isOn', 'isEnabled' ).to( command, 'value', 'isEnabled' );
|
||||
|
||||
// Execute the command when the button is clicked.
|
||||
command.listenTo( buttonView, 'execute', () => {
|
||||
editor.execute( `${ name }Command` );
|
||||
editor.editing.view.scrollToTheSelection();
|
||||
editor.editing.view.focus();
|
||||
} );
|
||||
|
||||
return buttonView;
|
||||
} );
|
||||
}
|
||||
}
|
27
_regroup/ckeditor5-mermaid/src/utils.js
Normal file
27
_regroup/ckeditor5-mermaid/src/utils.js
Normal file
@ -0,0 +1,27 @@
|
||||
/**
|
||||
* @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
|
||||
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
|
||||
*/
|
||||
|
||||
/**
|
||||
* @module mermaid/utils
|
||||
*/
|
||||
|
||||
/**
|
||||
* Helper function for setting the `isOn` state of buttons.
|
||||
*
|
||||
* @private
|
||||
* @param {module:core/editor/editor~Editor} editor
|
||||
* @param {String} commandName Short name of the command.
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
export function checkIsOn( editor, commandName ) {
|
||||
const selection = editor.model.document.selection;
|
||||
const mermaidItem = selection.getSelectedElement() || selection.getLastPosition().parent;
|
||||
|
||||
if ( mermaidItem && mermaidItem.is( 'element', 'mermaid' ) && mermaidItem.getAttribute( 'displayMode' ) === commandName ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
@ -0,0 +1,93 @@
|
||||
import { ClassicEditor } from '@ckeditor/ckeditor5-editor-classic';
|
||||
import { Paragraph } from '@ckeditor/ckeditor5-paragraph';
|
||||
import {
|
||||
_setModelData as setModelData,
|
||||
_getModelData as getModelData
|
||||
} from '@ckeditor/ckeditor5-engine';
|
||||
|
||||
import InsertMermaidCommand from '../../src/commands/insertMermaidCommand.js';
|
||||
import MermaidEditing from '../../src/mermaidediting.js';
|
||||
|
||||
/* global document */
|
||||
|
||||
describe( 'InsertMermaidCommand', () => {
|
||||
let domElement, editor, model, command;
|
||||
|
||||
beforeEach( async () => {
|
||||
domElement = document.createElement( 'div' );
|
||||
document.body.appendChild( domElement );
|
||||
|
||||
editor = await ClassicEditor.create( domElement, {
|
||||
plugins: [
|
||||
MermaidEditing,
|
||||
Paragraph
|
||||
]
|
||||
} );
|
||||
|
||||
model = editor.model;
|
||||
|
||||
command = new InsertMermaidCommand( editor );
|
||||
} );
|
||||
|
||||
afterEach( () => {
|
||||
domElement.remove();
|
||||
return editor.destroy();
|
||||
} );
|
||||
|
||||
describe( '#isEnabled', () => {
|
||||
describe( 'should be false', () => {
|
||||
it( 'when selection is inside mermaid', () => {
|
||||
setModelData( model,
|
||||
'<paragraph>foo</paragraph>' +
|
||||
'<mermaid source="flowchart TB\nA --> B\nB --> C">[]</mermaid>'
|
||||
);
|
||||
|
||||
expect( command.isEnabled ).to.be.false;
|
||||
} );
|
||||
|
||||
it( 'when mermaid is selected', () => {
|
||||
setModelData( model,
|
||||
'<paragraph>foo</paragraph>' +
|
||||
'[<mermaid source="flowchart TB\nA --> B\nB --> C"></mermaid>]'
|
||||
);
|
||||
|
||||
expect( command.isEnabled ).to.be.false;
|
||||
} );
|
||||
} );
|
||||
|
||||
describe( 'should be true', () => {
|
||||
it( 'when text is selected', () => {
|
||||
setModelData( model,
|
||||
'<paragraph>[foo]</paragraph>' +
|
||||
'<mermaid source="flowchart TB\nA --> B\nB --> C"></mermaid>'
|
||||
);
|
||||
|
||||
expect( command.isEnabled ).to.be.true;
|
||||
} );
|
||||
|
||||
it( 'when mermaid is part of the selection', () => {
|
||||
setModelData( model,
|
||||
'<paragraph>[foo</paragraph>' +
|
||||
'<mermaid source="flowchart TB\nA --> B\nB --> C"></mermaid>' +
|
||||
'<paragraph>b]az</paragraph>'
|
||||
);
|
||||
|
||||
expect( command.isEnabled ).to.be.true;
|
||||
} );
|
||||
} );
|
||||
} );
|
||||
|
||||
describe( 'execute()', () => {
|
||||
it( 'should add sample mermaid', () => {
|
||||
setModelData( model,
|
||||
'<paragraph>[foo]</paragraph>'
|
||||
);
|
||||
|
||||
command.execute();
|
||||
|
||||
expect( getModelData( model, { withoutSelection: true } ) ).to.equal(
|
||||
'<mermaid displayMode="split" source="flowchart TB\nA --> B\nB --> C"></mermaid>'
|
||||
);
|
||||
} );
|
||||
} );
|
||||
} );
|
@ -0,0 +1,113 @@
|
||||
import { ClassicEditor } from '@ckeditor/ckeditor5-editor-classic';
|
||||
import { Paragraph } from '@ckeditor/ckeditor5-paragraph';
|
||||
import {
|
||||
_setModelData as setModelData,
|
||||
_getModelData as getModelData
|
||||
} from '@ckeditor/ckeditor5-engine';
|
||||
|
||||
import MermaidPreviewCommand from '../../src/commands/mermaidPreviewCommand.js';
|
||||
import MermaidEditing from '../../src/mermaidediting.js';
|
||||
|
||||
/* global document */
|
||||
|
||||
describe( 'MermaidPreviewCommand', () => {
|
||||
let domElement, editor, model, command;
|
||||
|
||||
beforeEach( async () => {
|
||||
domElement = document.createElement( 'div' );
|
||||
document.body.appendChild( domElement );
|
||||
|
||||
editor = await ClassicEditor.create( domElement, {
|
||||
plugins: [
|
||||
MermaidEditing,
|
||||
Paragraph
|
||||
]
|
||||
} );
|
||||
|
||||
model = editor.model;
|
||||
|
||||
command = new MermaidPreviewCommand( editor );
|
||||
} );
|
||||
|
||||
afterEach( () => {
|
||||
domElement.remove();
|
||||
return editor.destroy();
|
||||
} );
|
||||
|
||||
describe( '#value', () => {
|
||||
it( 'should be true when mermaid element has displayMode attribute equal to "preview"', () => {
|
||||
setModelData( model, '<mermaid displayMode="preview" source="foo"></mermaid>' );
|
||||
|
||||
expect( command.value ).to.equal( true );
|
||||
} );
|
||||
|
||||
it( 'should be false when mermaid element has displayMode attribute equal to "split"', () => {
|
||||
setModelData( model, '<mermaid displayMode="split" source="foo"></mermaid>' );
|
||||
|
||||
expect( command.value ).to.equal( false );
|
||||
} );
|
||||
|
||||
it( 'should be false when mermaid element has displayMode attribute equal to "source"', () => {
|
||||
setModelData( model, '<mermaid displayMode="source" source="foo"></mermaid>' );
|
||||
|
||||
expect( command.value ).to.equal( false );
|
||||
} );
|
||||
} );
|
||||
|
||||
describe( '#isEnabled', () => {
|
||||
describe( 'should be false', () => {
|
||||
it( 'when text is selected', () => {
|
||||
setModelData( model,
|
||||
'<paragraph>[foo]</paragraph>' +
|
||||
'<mermaid source="flowchart TB\nA --> B\nB --> C"></mermaid>'
|
||||
);
|
||||
|
||||
expect( command.isEnabled ).to.be.false;
|
||||
} );
|
||||
|
||||
it( 'when mermaid is part of the selection', () => {
|
||||
setModelData( model,
|
||||
'<paragraph>[foo</paragraph>' +
|
||||
'<mermaid source="flowchart TB\nA --> B\nB --> C"></mermaid>' +
|
||||
'<paragraph>b]az</paragraph>'
|
||||
);
|
||||
|
||||
expect( command.isEnabled ).to.be.false;
|
||||
} );
|
||||
} );
|
||||
|
||||
describe( 'should be true', () => {
|
||||
it( 'when selection is inside mermaid', () => {
|
||||
setModelData( model,
|
||||
'<paragraph>foo</paragraph>' +
|
||||
'<mermaid source="flowchart TB\nA --> B\nB --> C">[]</mermaid>'
|
||||
);
|
||||
|
||||
expect( command.isEnabled ).to.be.true;
|
||||
} );
|
||||
|
||||
it( 'when mermaid is selected', () => {
|
||||
setModelData( model,
|
||||
'<paragraph>foo</paragraph>' +
|
||||
'[<mermaid source="flowchart TB\nA --> B\nB --> C"></mermaid>]'
|
||||
);
|
||||
|
||||
expect( command.isEnabled ).to.be.true;
|
||||
} );
|
||||
} );
|
||||
} );
|
||||
|
||||
describe( 'execute()', () => {
|
||||
it( 'should change displayMode to "preview" for mermaid', () => {
|
||||
setModelData( model,
|
||||
'[<mermaid displayMode="source" source="foo"></mermaid>]'
|
||||
);
|
||||
|
||||
command.execute();
|
||||
|
||||
expect( getModelData( model ) ).to.equal(
|
||||
'[<mermaid displayMode="preview" source="foo"></mermaid>]'
|
||||
);
|
||||
} );
|
||||
} );
|
||||
} );
|
@ -0,0 +1,113 @@
|
||||
import { ClassicEditor } from '@ckeditor/ckeditor5-editor-classic';
|
||||
import { Paragraph } from '@ckeditor/ckeditor5-paragraph';
|
||||
import {
|
||||
_setModelData as setModelData,
|
||||
_getModelData as getModelData
|
||||
} from '@ckeditor/ckeditor5-engine';
|
||||
|
||||
import MermaidSourceViewCommand from '../../src/commands/mermaidSourceViewCommand.js';
|
||||
import MermaidEditing from '../../src/mermaidediting.js';
|
||||
|
||||
/* global document */
|
||||
|
||||
describe( 'MermaidSourceViewCommand', () => {
|
||||
let domElement, editor, model, command;
|
||||
|
||||
beforeEach( async () => {
|
||||
domElement = document.createElement( 'div' );
|
||||
document.body.appendChild( domElement );
|
||||
|
||||
editor = await ClassicEditor.create( domElement, {
|
||||
plugins: [
|
||||
MermaidEditing,
|
||||
Paragraph
|
||||
]
|
||||
} );
|
||||
|
||||
model = editor.model;
|
||||
|
||||
command = new MermaidSourceViewCommand( editor );
|
||||
} );
|
||||
|
||||
afterEach( () => {
|
||||
domElement.remove();
|
||||
return editor.destroy();
|
||||
} );
|
||||
|
||||
describe( '#value', () => {
|
||||
it( 'should be true when mermaid element has displayMode attribute equal to "preview"', () => {
|
||||
setModelData( model, '<mermaid displayMode="preview" source="foo"></mermaid>' );
|
||||
|
||||
expect( command.value ).to.equal( false );
|
||||
} );
|
||||
|
||||
it( 'should be false when mermaid element has displayMode attribute equal to "split"', () => {
|
||||
setModelData( model, '<mermaid displayMode="split" source="foo"></mermaid>' );
|
||||
|
||||
expect( command.value ).to.equal( false );
|
||||
} );
|
||||
|
||||
it( 'should be false when mermaid element has displayMode attribute equal to "source"', () => {
|
||||
setModelData( model, '<mermaid displayMode="source" source="foo"></mermaid>' );
|
||||
|
||||
expect( command.value ).to.equal( true );
|
||||
} );
|
||||
} );
|
||||
|
||||
describe( '#isEnabled', () => {
|
||||
describe( 'should be false', () => {
|
||||
it( 'when text is selected', () => {
|
||||
setModelData( model,
|
||||
'<paragraph>[foo]</paragraph>' +
|
||||
'<mermaid source="flowchart TB\nA --> B\nB --> C"></mermaid>'
|
||||
);
|
||||
|
||||
expect( command.isEnabled ).to.be.false;
|
||||
} );
|
||||
|
||||
it( 'when mermaid is part of the selection', () => {
|
||||
setModelData( model,
|
||||
'<paragraph>[foo</paragraph>' +
|
||||
'<mermaid source="flowchart TB\nA --> B\nB --> C"></mermaid>' +
|
||||
'<paragraph>b]az</paragraph>'
|
||||
);
|
||||
|
||||
expect( command.isEnabled ).to.be.false;
|
||||
} );
|
||||
} );
|
||||
|
||||
describe( 'should be true', () => {
|
||||
it( 'when selection is inside mermaid', () => {
|
||||
setModelData( model,
|
||||
'<paragraph>foo</paragraph>' +
|
||||
'<mermaid source="flowchart TB\nA --> B\nB --> C">[]</mermaid>'
|
||||
);
|
||||
|
||||
expect( command.isEnabled ).to.be.true;
|
||||
} );
|
||||
|
||||
it( 'when mermaid is selected', () => {
|
||||
setModelData( model,
|
||||
'<paragraph>foo</paragraph>' +
|
||||
'[<mermaid source="flowchart TB\nA --> B\nB --> C"></mermaid>]'
|
||||
);
|
||||
|
||||
expect( command.isEnabled ).to.be.true;
|
||||
} );
|
||||
} );
|
||||
} );
|
||||
|
||||
describe( 'execute()', () => {
|
||||
it( 'should add text', () => {
|
||||
setModelData( model,
|
||||
'[<mermaid displayMode="preview" source="foo"></mermaid>]'
|
||||
);
|
||||
|
||||
command.execute();
|
||||
|
||||
expect( getModelData( model ) ).to.equal(
|
||||
'[<mermaid displayMode="source" source="foo"></mermaid>]'
|
||||
);
|
||||
} );
|
||||
} );
|
||||
} );
|
@ -0,0 +1,113 @@
|
||||
import { ClassicEditor } from '@ckeditor/ckeditor5-editor-classic';
|
||||
import { Paragraph } from '@ckeditor/ckeditor5-paragraph';
|
||||
import {
|
||||
_setModelData as setModelData,
|
||||
_getModelData as getModelData
|
||||
} from '@ckeditor/ckeditor5-engine';
|
||||
|
||||
import MermaidSplitViewCommand from '../../src/commands/mermaidSplitViewCommand.js';
|
||||
import MermaidEditing from '../../src/mermaidediting.js';
|
||||
|
||||
/* global document */
|
||||
|
||||
describe( 'MermaidSplitViewCommand', () => {
|
||||
let domElement, editor, model, command;
|
||||
|
||||
beforeEach( async () => {
|
||||
domElement = document.createElement( 'div' );
|
||||
document.body.appendChild( domElement );
|
||||
|
||||
editor = await ClassicEditor.create( domElement, {
|
||||
plugins: [
|
||||
MermaidEditing,
|
||||
Paragraph
|
||||
]
|
||||
} );
|
||||
|
||||
model = editor.model;
|
||||
|
||||
command = new MermaidSplitViewCommand( editor );
|
||||
} );
|
||||
|
||||
afterEach( () => {
|
||||
domElement.remove();
|
||||
return editor.destroy();
|
||||
} );
|
||||
|
||||
describe( '#value', () => {
|
||||
it( 'should be true when mermaid element has displayMode attribute equal to "preview"', () => {
|
||||
setModelData( model, '<mermaid displayMode="preview" source="foo"></mermaid>' );
|
||||
|
||||
expect( command.value ).to.equal( false );
|
||||
} );
|
||||
|
||||
it( 'should be false when mermaid element has displayMode attribute equal to "split"', () => {
|
||||
setModelData( model, '<mermaid displayMode="split" source="foo"></mermaid>' );
|
||||
|
||||
expect( command.value ).to.equal( true );
|
||||
} );
|
||||
|
||||
it( 'should be false when mermaid element has source attribute equal to "source"', () => {
|
||||
setModelData( model, '<mermaid displayMode="source" source="foo"></mermaid>' );
|
||||
|
||||
expect( command.value ).to.equal( false );
|
||||
} );
|
||||
} );
|
||||
|
||||
describe( '#isEnabled', () => {
|
||||
describe( 'should be false', () => {
|
||||
it( 'when text is selected', () => {
|
||||
setModelData( model,
|
||||
'<paragraph>[foo]</paragraph>' +
|
||||
'<mermaid source="flowchart TB\nA --> B\nB --> C"></mermaid>'
|
||||
);
|
||||
|
||||
expect( command.isEnabled ).to.be.false;
|
||||
} );
|
||||
|
||||
it( 'when mermaid is part of the selection', () => {
|
||||
setModelData( model,
|
||||
'<paragraph>[foo</paragraph>' +
|
||||
'<mermaid source="flowchart TB\nA --> B\nB --> C"></mermaid>' +
|
||||
'<paragraph>b]az</paragraph>'
|
||||
);
|
||||
|
||||
expect( command.isEnabled ).to.be.false;
|
||||
} );
|
||||
} );
|
||||
|
||||
describe( 'should be true', () => {
|
||||
it( 'when selection is inside mermaid', () => {
|
||||
setModelData( model,
|
||||
'<paragraph>foo</paragraph>' +
|
||||
'<mermaid source="flowchart TB\nA --> B\nB --> C">[]</mermaid>'
|
||||
);
|
||||
|
||||
expect( command.isEnabled ).to.be.true;
|
||||
} );
|
||||
|
||||
it( 'when mermaid is selected', () => {
|
||||
setModelData( model,
|
||||
'<paragraph>foo</paragraph>' +
|
||||
'[<mermaid source="flowchart TB\nA --> B\nB --> C"></mermaid>]'
|
||||
);
|
||||
|
||||
expect( command.isEnabled ).to.be.true;
|
||||
} );
|
||||
} );
|
||||
} );
|
||||
|
||||
describe( 'execute()', () => {
|
||||
it( 'should change displayMode to "source" for mermaid', () => {
|
||||
setModelData( model,
|
||||
'[<mermaid displayMode="source" source="foo"></mermaid>]'
|
||||
);
|
||||
|
||||
command.execute();
|
||||
|
||||
expect( getModelData( model ) ).to.equal(
|
||||
'[<mermaid displayMode="split" source="foo"></mermaid>]'
|
||||
);
|
||||
} );
|
||||
} );
|
||||
} );
|
32
_regroup/ckeditor5-mermaid/tests/index.js
Normal file
32
_regroup/ckeditor5-mermaid/tests/index.js
Normal file
@ -0,0 +1,32 @@
|
||||
import { Mermaid as MermaidDll, icons } from '../src/index.js';
|
||||
import Mermaid from '../src/mermaid.js';
|
||||
|
||||
import infoIcon from './../theme/icons/info.svg';
|
||||
import insertMermaidIcon from './../theme/icons/insert.svg';
|
||||
import previewModeIcon from './../theme/icons/preview-mode.svg';
|
||||
import splitModeIcon from './../theme/icons/split-mode.svg';
|
||||
import sourceModeIcon from './../theme/icons/source-mode.svg';
|
||||
|
||||
describe( 'CKEditor5 Mermaid DLL', () => {
|
||||
it( 'exports MermaidWidget', () => {
|
||||
expect( MermaidDll ).to.equal( Mermaid );
|
||||
} );
|
||||
|
||||
describe( 'icons', () => {
|
||||
it( 'exports the "insertMermaidIcon" icon', () => {
|
||||
expect( icons.insertMermaidIcon ).to.equal( insertMermaidIcon );
|
||||
} );
|
||||
it( 'exports the "infoIcon" icon', () => {
|
||||
expect( icons.infoIcon ).to.equal( infoIcon );
|
||||
} );
|
||||
it( 'exports the "previewModeIcon" icon', () => {
|
||||
expect( icons.previewModeIcon ).to.equal( previewModeIcon );
|
||||
} );
|
||||
it( 'exports the "splitModeIcon" icon', () => {
|
||||
expect( icons.splitModeIcon ).to.equal( splitModeIcon );
|
||||
} );
|
||||
it( 'exports the "sourceModeIcon" icon', () => {
|
||||
expect( icons.sourceModeIcon ).to.equal( sourceModeIcon );
|
||||
} );
|
||||
} );
|
||||
} );
|
67
_regroup/ckeditor5-mermaid/tests/manual/markdown.html
Normal file
67
_regroup/ckeditor5-mermaid/tests/manual/markdown.html
Normal file
@ -0,0 +1,67 @@
|
||||
<style type="text/css">
|
||||
#editor, .ck-editor {
|
||||
/* Adjust width to the typical width in GH. */
|
||||
width: 820px !important;
|
||||
}
|
||||
|
||||
pre.markdown-output {
|
||||
background: hsl(70, 7%, 16%);
|
||||
color: hsl(0, 0%, 100%);
|
||||
display: block;
|
||||
font-size: 1em;
|
||||
font-family: Monaco, Menlo, Consolas, "Roboto Mono", "Courier New", "Ubuntu Mono", monospace;
|
||||
padding: 1.333em;
|
||||
}
|
||||
|
||||
.editor-container {
|
||||
display:flex;
|
||||
}
|
||||
|
||||
.output-container {
|
||||
padding-left: 20px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="editor-container">
|
||||
<textarea id="editor">
|
||||
Mermaid snippet:
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
A --> C
|
||||
A --> D
|
||||
B --> C
|
||||
B --> D
|
||||
```
|
||||
|
||||
More complex case:
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Alice
|
||||
participant Bob
|
||||
Alice->>John: Hello John, how are you?
|
||||
loop Healthcheck
|
||||
John->>John: Fight against hypochondria
|
||||
end
|
||||
Note right of John: Rational thoughts <br/>prevail!
|
||||
John-->>Alice: Great!
|
||||
John->>Bob: How about you?
|
||||
Bob-->>John: Jolly good!
|
||||
```
|
||||
|
||||
Javascript snippet:
|
||||
|
||||
```Javascript
|
||||
var foo = 'bar';
|
||||
|
||||
alert( foo );
|
||||
```
|
||||
</textarea>
|
||||
|
||||
<div class="output-container">
|
||||
<p>Output:</p>
|
||||
|
||||
<pre class="markdown-output"><code id="markdown-output"></code></pre>
|
||||
</div>
|
||||
</div>
|
67
_regroup/ckeditor5-mermaid/tests/manual/markdown.js
Normal file
67
_regroup/ckeditor5-mermaid/tests/manual/markdown.js
Normal file
@ -0,0 +1,67 @@
|
||||
/**
|
||||
* @license Copyright (c) 2003-2021, CKSource - Frederico Knabben. All rights reserved.
|
||||
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
|
||||
*/
|
||||
|
||||
/* globals console, window, document */
|
||||
|
||||
import { ClassicEditor } from '@ckeditor/ckeditor5-editor-classic';
|
||||
import { Typing } from '@ckeditor/ckeditor5-typing';
|
||||
import { Paragraph } from '@ckeditor/ckeditor5-paragraph';
|
||||
import { Undo } from '@ckeditor/ckeditor5-undo';
|
||||
import { Enter } from '@ckeditor/ckeditor5-enter';
|
||||
import { Clipboard } from '@ckeditor/ckeditor5-clipboard';
|
||||
import { Link } from '@ckeditor/ckeditor5-link';
|
||||
import { Bold, Italic } from '@ckeditor/ckeditor5-basic-styles';
|
||||
import { Markdown } from '@ckeditor/ckeditor5-markdown-gfm';
|
||||
import { CodeBlock } from '@ckeditor/ckeditor5-code-block';
|
||||
|
||||
import Mermaid from '../../src/mermaid.js';
|
||||
|
||||
ClassicEditor
|
||||
.create( document.querySelector( '#editor' ), {
|
||||
plugins: [
|
||||
Markdown,
|
||||
Typing,
|
||||
Paragraph,
|
||||
Undo,
|
||||
Enter,
|
||||
Clipboard,
|
||||
Link,
|
||||
Bold,
|
||||
Italic,
|
||||
CodeBlock,
|
||||
Mermaid
|
||||
],
|
||||
toolbar: [ 'bold', 'italic', 'link', 'undo', 'redo', 'codeBlock', 'mermaid' ],
|
||||
codeBlock: {
|
||||
languages: [
|
||||
{ language: 'plaintext', label: 'Plain text', class: '' },
|
||||
{ language: 'javascript', label: 'JavaScript' },
|
||||
{ language: 'python', label: 'Python' },
|
||||
{ language: 'mermaid', label: 'Mermaid' }
|
||||
]
|
||||
}
|
||||
|
||||
} )
|
||||
.then( editor => {
|
||||
window.editor = editor;
|
||||
|
||||
setupMarkdownOutputPreview( editor );
|
||||
} )
|
||||
.catch( err => {
|
||||
console.error( err.stack );
|
||||
} );
|
||||
|
||||
function setupMarkdownOutputPreview( editor ) {
|
||||
const outputElement = document.querySelector( '#markdown-output' );
|
||||
|
||||
editor.model.document.on( 'change', () => {
|
||||
outputElement.innerText = editor.getData();
|
||||
} );
|
||||
|
||||
// Set the initial data with delay so hightlight.js doesn't catch them.
|
||||
window.setTimeout( () => {
|
||||
outputElement.innerText = editor.getData();
|
||||
}, 500 );
|
||||
}
|
1
_regroup/ckeditor5-mermaid/tests/manual/markdown.md
Normal file
1
_regroup/ckeditor5-mermaid/tests/manual/markdown.md
Normal file
@ -0,0 +1 @@
|
||||
## Mermaid widget
|
38
_regroup/ckeditor5-mermaid/tests/manual/mermaid.html
Normal file
38
_regroup/ckeditor5-mermaid/tests/manual/mermaid.html
Normal file
@ -0,0 +1,38 @@
|
||||
<style type="text/css">
|
||||
#editor, .ck-editor {
|
||||
/* Adjust width to the typical width in GH. */
|
||||
width: 820px !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="editor">
|
||||
<p>Mermaid snippet:</p>
|
||||
|
||||
<pre>
|
||||
<code class="mermaid language-mermaid">
|
||||
flowchart TB
|
||||
A --> C
|
||||
A --> D
|
||||
B --> C
|
||||
B --> D
|
||||
</code>
|
||||
</pre>
|
||||
|
||||
<p>More complex case:</p>
|
||||
|
||||
<pre>
|
||||
<code class="mermaid language-mermaid">
|
||||
sequenceDiagram
|
||||
participant Alice
|
||||
participant Bob
|
||||
Alice->>John: Hello John, how are you?
|
||||
loop Healthcheck
|
||||
John->>John: Fight against hypochondria
|
||||
end
|
||||
Note right of John: Rational thoughts <br/>prevail!
|
||||
John-->>Alice: Great!
|
||||
John->>Bob: How about you?
|
||||
Bob-->>John: Jolly good!
|
||||
</code>
|
||||
</pre>
|
||||
</div>
|
50
_regroup/ckeditor5-mermaid/tests/manual/mermaid.js
Normal file
50
_regroup/ckeditor5-mermaid/tests/manual/mermaid.js
Normal file
@ -0,0 +1,50 @@
|
||||
/**
|
||||
* @license Copyright (c) 2003-2021, CKSource - Frederico Knabben. All rights reserved.
|
||||
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
|
||||
*/
|
||||
|
||||
/* globals console, window, document */
|
||||
|
||||
import { ClassicEditor } from '@ckeditor/ckeditor5-editor-classic';
|
||||
import { Typing } from '@ckeditor/ckeditor5-typing';
|
||||
import { Paragraph } from '@ckeditor/ckeditor5-paragraph';
|
||||
import { Undo } from '@ckeditor/ckeditor5-undo';
|
||||
import { Enter } from '@ckeditor/ckeditor5-enter';
|
||||
import { Clipboard } from '@ckeditor/ckeditor5-clipboard';
|
||||
import { Link } from '@ckeditor/ckeditor5-link';
|
||||
import { Bold, Italic } from '@ckeditor/ckeditor5-basic-styles';
|
||||
import { CodeBlock } from '@ckeditor/ckeditor5-code-block';
|
||||
|
||||
import Mermaid from '../../src/mermaid.js';
|
||||
|
||||
ClassicEditor
|
||||
.create( document.querySelector( '#editor' ), {
|
||||
plugins: [
|
||||
Typing,
|
||||
Paragraph,
|
||||
Undo,
|
||||
Enter,
|
||||
Clipboard,
|
||||
Link,
|
||||
Bold,
|
||||
Italic,
|
||||
CodeBlock,
|
||||
Mermaid
|
||||
],
|
||||
toolbar: [ 'bold', 'italic', 'link', 'undo', 'redo', 'codeBlock', 'mermaid' ],
|
||||
codeBlock: {
|
||||
languages: [
|
||||
{ language: 'plaintext', label: 'Plain text', class: '' },
|
||||
{ language: 'javascript', label: 'JavaScript' },
|
||||
{ language: 'python', label: 'Python' },
|
||||
{ language: 'mermaid', label: 'Mermaid' }
|
||||
]
|
||||
}
|
||||
|
||||
} )
|
||||
.then( editor => {
|
||||
window.editor = editor;
|
||||
} )
|
||||
.catch( err => {
|
||||
console.error( err.stack );
|
||||
} );
|
1
_regroup/ckeditor5-mermaid/tests/manual/mermaid.md
Normal file
1
_regroup/ckeditor5-mermaid/tests/manual/mermaid.md
Normal file
@ -0,0 +1 @@
|
||||
## Mermaid widget
|
47
_regroup/ckeditor5-mermaid/tests/mermaid.js
Normal file
47
_regroup/ckeditor5-mermaid/tests/mermaid.js
Normal file
@ -0,0 +1,47 @@
|
||||
import { ClassicEditor } from '@ckeditor/ckeditor5-editor-classic';
|
||||
import { Essentials } from '@ckeditor/ckeditor5-essentials';
|
||||
import { Paragraph } from '@ckeditor/ckeditor5-paragraph';
|
||||
import { Heading } from '@ckeditor/ckeditor5-heading';
|
||||
import { _setModelData as setModelData } from '@ckeditor/ckeditor5-engine';
|
||||
|
||||
import Mermaid from '../src/mermaid.js';
|
||||
|
||||
/* global document */
|
||||
|
||||
describe( 'Mermaid', () => {
|
||||
it( 'should be named', () => {
|
||||
expect( Mermaid.pluginName ).to.equal( 'Mermaid' );
|
||||
} );
|
||||
|
||||
describe( 'init()', () => {
|
||||
let domElement, editor;
|
||||
|
||||
beforeEach( async () => {
|
||||
domElement = document.createElement( 'div' );
|
||||
document.body.appendChild( domElement );
|
||||
|
||||
editor = await ClassicEditor.create( domElement, {
|
||||
plugins: [
|
||||
Paragraph,
|
||||
Heading,
|
||||
Essentials,
|
||||
Mermaid
|
||||
],
|
||||
toolbar: [
|
||||
'mermaid'
|
||||
]
|
||||
} );
|
||||
|
||||
setModelData( editor.model, '<paragraph>[]</paragraph>' );
|
||||
} );
|
||||
|
||||
afterEach( () => {
|
||||
domElement.remove();
|
||||
return editor.destroy();
|
||||
} );
|
||||
|
||||
it( 'should add an icon to the toolbar', () => {
|
||||
expect( editor.ui.componentFactory.has( 'Mermaid' ) ).to.equal( true );
|
||||
} );
|
||||
} );
|
||||
} );
|
296
_regroup/ckeditor5-mermaid/tests/mermaidediting.js
Normal file
296
_regroup/ckeditor5-mermaid/tests/mermaidediting.js
Normal file
@ -0,0 +1,296 @@
|
||||
import { ClassicEditor } from '@ckeditor/ckeditor5-editor-classic';
|
||||
import { Essentials } from '@ckeditor/ckeditor5-essentials';
|
||||
import { Paragraph } from '@ckeditor/ckeditor5-paragraph';
|
||||
import { Heading } from '@ckeditor/ckeditor5-heading';
|
||||
import { CodeBlockEditing } from '@ckeditor/ckeditor5-code-block';
|
||||
import {
|
||||
_setModelData as setModelData,
|
||||
_getModelData as getModelData,
|
||||
_getViewData as getViewData
|
||||
} from '@ckeditor/ckeditor5-engine';
|
||||
import MermaidEditing from '../src/mermaidediting.js';
|
||||
|
||||
/* global document */
|
||||
|
||||
describe( 'MermaidEditing', () => {
|
||||
it( 'should be named', () => {
|
||||
expect( MermaidEditing.pluginName ).to.equal( 'MermaidEditing' );
|
||||
} );
|
||||
|
||||
describe( 'conversion', () => {
|
||||
let domElement, editor, model;
|
||||
|
||||
beforeEach( async () => {
|
||||
domElement = document.createElement( 'div' );
|
||||
document.body.appendChild( domElement );
|
||||
|
||||
editor = await ClassicEditor.create( domElement, {
|
||||
plugins: [
|
||||
Paragraph,
|
||||
Heading,
|
||||
Essentials,
|
||||
CodeBlockEditing,
|
||||
MermaidEditing
|
||||
]
|
||||
} );
|
||||
|
||||
model = editor.model;
|
||||
} );
|
||||
|
||||
afterEach( () => {
|
||||
domElement.remove();
|
||||
return editor.destroy();
|
||||
} );
|
||||
|
||||
describe( 'conversion', () => {
|
||||
describe( 'upcast', () => {
|
||||
it( 'works correctly', () => {
|
||||
editor.setData(
|
||||
'<pre spellcheck="false">' +
|
||||
'<code class="language-mermaid">flowchart TB\nA --> B\nB --> C</code>' +
|
||||
'</pre>'
|
||||
);
|
||||
|
||||
expect( getModelData( model, { withoutSelection: true } ) ).to.equal(
|
||||
'<mermaid displayMode="split" source="flowchart TB\nA --> B\nB --> C">' +
|
||||
'</mermaid>'
|
||||
);
|
||||
} );
|
||||
|
||||
it( 'works correctly when empty', () => {
|
||||
editor.setData(
|
||||
'<pre spellcheck="false">' +
|
||||
'<code class="language-mermaid"></code>' +
|
||||
'</pre>'
|
||||
);
|
||||
|
||||
expect( getModelData( model, { withoutSelection: true } ) ).to.equal(
|
||||
'<mermaid displayMode="split" source=""></mermaid>'
|
||||
);
|
||||
} );
|
||||
} );
|
||||
|
||||
describe( 'data downcast', () => {
|
||||
it( 'works correctly', () => {
|
||||
// Using editor.setData() instead of setModelData helper because of #11365.
|
||||
editor.setData(
|
||||
'<pre spellcheck="false">' +
|
||||
'<code class="language-mermaid">flowchart TB\nA --> B\nB --> C</code>' +
|
||||
'</pre>'
|
||||
);
|
||||
|
||||
expect( editor.getData() ).to.equal(
|
||||
'<pre spellcheck="false">' +
|
||||
'<code class="language-mermaid">flowchart TB\nA --> B\nB --> C</code>' +
|
||||
'</pre>'
|
||||
);
|
||||
} );
|
||||
|
||||
it( 'works correctly when empty ', () => {
|
||||
// Using editor.setData() instead of setModelData helper because of #11365.
|
||||
editor.setData(
|
||||
'<pre spellcheck="false">' +
|
||||
'<code class="language-mermaid"></code>' +
|
||||
'</pre>'
|
||||
);
|
||||
|
||||
expect( editor.getData() ).to.equal(
|
||||
'<pre spellcheck="false">' +
|
||||
'<code class="language-mermaid"></code>' +
|
||||
'</pre>'
|
||||
);
|
||||
} );
|
||||
} );
|
||||
|
||||
describe( 'editing downcast', () => {
|
||||
it( 'works correctly without displayMode attribute', () => {
|
||||
// Using editor.setData() instead of setModelData helper because of #11365.
|
||||
editor.setData(
|
||||
'<pre spellcheck="false">' +
|
||||
'<code class="language-mermaid">flowchart TB\nA --> B\nB --> C</code>' +
|
||||
'</pre>'
|
||||
);
|
||||
|
||||
expect( getViewData( editor.editing.view, { withoutSelection: true } ) ).to.equal(
|
||||
'<div class="ck-mermaid__split-mode ck-mermaid__wrapper ck-widget ck-widget_selected' +
|
||||
' ck-widget_with-selection-handle" contenteditable="false">' +
|
||||
'<div class="ck ck-widget__selection-handle"></div>' +
|
||||
// New lines replaced with space, same issue in getViewData as in #11365.
|
||||
'<textarea class="ck-mermaid__editing-view" data-cke-ignore-events="true"' +
|
||||
' placeholder="Insert mermaid source code"></textarea>' +
|
||||
'<div class="ck-mermaid__preview"></div>' +
|
||||
'<div class="ck ck-reset_all ck-widget__type-around"></div>' +
|
||||
'</div>'
|
||||
);
|
||||
} );
|
||||
|
||||
it( 'works correctly with displayMode attribute', () => {
|
||||
setModelData( editor.model,
|
||||
'<mermaid source="foo" displayMode="preview"></mermaid>'
|
||||
);
|
||||
|
||||
expect( getViewData( editor.editing.view, { withoutSelection: true } ) ).to.equal(
|
||||
'<div class="ck-mermaid__preview-mode ck-mermaid__wrapper ck-widget ck-widget_selected ' +
|
||||
'ck-widget_with-selection-handle" contenteditable="false">' +
|
||||
'<div class="ck ck-widget__selection-handle"></div>' +
|
||||
'<textarea class="ck-mermaid__editing-view" data-cke-ignore-events="true"' +
|
||||
' placeholder="Insert mermaid source code"></textarea>' +
|
||||
'<div class="ck-mermaid__preview"></div>' +
|
||||
'<div class="ck ck-reset_all ck-widget__type-around"></div>' +
|
||||
'</div>'
|
||||
);
|
||||
} );
|
||||
|
||||
it( 'works correctly with empty source', () => {
|
||||
setModelData( editor.model,
|
||||
'<mermaid source="" displayMode="preview"></mermaid>'
|
||||
);
|
||||
|
||||
expect( getViewData( editor.editing.view, { withoutSelection: true } ) ).to.equal(
|
||||
'<div class="ck-mermaid__preview-mode ck-mermaid__wrapper ck-widget ck-widget_selected ' +
|
||||
'ck-widget_with-selection-handle" contenteditable="false">' +
|
||||
'<div class="ck ck-widget__selection-handle"></div>' +
|
||||
'<textarea class="ck-mermaid__editing-view" data-cke-ignore-events="true"' +
|
||||
' placeholder="Insert mermaid source code"></textarea>' +
|
||||
'<div class="ck-mermaid__preview"></div>' +
|
||||
'<div class="ck ck-reset_all ck-widget__type-around"></div>' +
|
||||
'</div>'
|
||||
);
|
||||
} );
|
||||
|
||||
describe( 'textarea value', () => {
|
||||
let domTextarea = null;
|
||||
|
||||
beforeEach( () => {
|
||||
// Using editor.setData() instead of setModelData helper because of #11365.
|
||||
editor.setData(
|
||||
'<pre spellcheck="false">' +
|
||||
'<code class="language-mermaid">flowchart TB\nA --> B\nB --> C</code>' +
|
||||
'</pre>'
|
||||
);
|
||||
|
||||
const textareaView = editor.editing.view.document.getRoot().getChild( 0 ).getChild( 1 );
|
||||
domTextarea = editor.editing.view.domConverter.viewToDom( textareaView );
|
||||
} );
|
||||
|
||||
it( 'is properly set during the initial conversion', () => {
|
||||
expect( domTextarea.value ).to.equal( 'flowchart TB\nA --> B\nB --> C' );
|
||||
} );
|
||||
|
||||
it( 'is properly updated after model\'s attribute change', () => {
|
||||
const { model } = editor;
|
||||
|
||||
const mermaidModel = model.document.getRoot().getChild( 0 );
|
||||
|
||||
model.change( writer => {
|
||||
writer.setAttribute( 'source', 'abc', mermaidModel );
|
||||
} );
|
||||
|
||||
expect( domTextarea.value ).to.equal( 'abc' );
|
||||
} );
|
||||
|
||||
it( 'doesn\'t loop if model attribute changes to the same value', () => {
|
||||
const { model } = editor;
|
||||
|
||||
const mermaidModel = model.document.getRoot().getChild( 0 );
|
||||
|
||||
model.change( writer => {
|
||||
writer.setAttribute( 'source', 'flowchart TB\nA --> B\nB --> C', mermaidModel );
|
||||
} );
|
||||
|
||||
expect( domTextarea.value ).to.equal( 'flowchart TB\nA --> B\nB --> C' );
|
||||
} );
|
||||
} );
|
||||
|
||||
describe( 'preview div', () => {
|
||||
let domPreviewContainer, renderMermaidStub;
|
||||
|
||||
beforeEach( () => {
|
||||
// Using editor.setData() instead of setModelData helper because of #11365.
|
||||
editor.setData(
|
||||
'<pre spellcheck="false">' +
|
||||
'<code class="language-mermaid">flowchart TB\nA --> B\nB --> C</code>' +
|
||||
'</pre>'
|
||||
);
|
||||
|
||||
const previewContainerView = editor.editing.view.document.getRoot().getChild( 0 ).getChild( 2 );
|
||||
domPreviewContainer = editor.editing.view.domConverter.viewToDom( previewContainerView );
|
||||
|
||||
renderMermaidStub = sinon.stub( editor.plugins.get( 'MermaidEditing' ), '_renderMermaid' );
|
||||
} );
|
||||
|
||||
afterEach( () => {
|
||||
renderMermaidStub.restore();
|
||||
} );
|
||||
|
||||
it( 'has proper inner text set during the initial conversion', () => {
|
||||
expect( domPreviewContainer.textContent ).to.equal( 'flowchart TB\nA --> B\nB --> C' );
|
||||
} );
|
||||
|
||||
it( 'has proper inner text set after a model\'s attribute change', () => {
|
||||
const { model } = editor;
|
||||
|
||||
const mermaidModel = model.document.getRoot().getChild( 0 );
|
||||
|
||||
model.change( writer => {
|
||||
writer.setAttribute( 'source', 'abc', mermaidModel );
|
||||
} );
|
||||
|
||||
expect( domPreviewContainer.textContent ).to.equal( 'abc' );
|
||||
} );
|
||||
|
||||
it( 'calls mermaid render function after a model\'s attribute change', () => {
|
||||
const { model } = editor;
|
||||
|
||||
const mermaidModel = model.document.getRoot().getChild( 0 );
|
||||
|
||||
model.change( writer => {
|
||||
writer.setAttribute( 'source', 'abc', mermaidModel );
|
||||
} );
|
||||
|
||||
expect( renderMermaidStub.callCount ).to.equal( 1 );
|
||||
sinon.assert.calledWithExactly( renderMermaidStub, domPreviewContainer );
|
||||
} );
|
||||
} );
|
||||
} );
|
||||
|
||||
it( 'adds a editing pipeline converter that has a precedence over code block', () => {
|
||||
setModelData( editor.model, '<mermaid source="foo"></mermaid>' );
|
||||
|
||||
const firstViewChild = editor.editing.view.document.getRoot().getChild( 0 );
|
||||
|
||||
expect( firstViewChild.name ).to.equal( 'div' );
|
||||
expect( firstViewChild.hasClass( 'ck-mermaid__wrapper' ), 'has ck-mermaid__wrapper class' ).to.be.true;
|
||||
} );
|
||||
|
||||
it( 'does not convert code blocks other than mermaid language', () => {
|
||||
setModelData( editor.model, '<codeBlock language="javascript">foo</codeBlock>' );
|
||||
|
||||
const firstViewChild = editor.editing.view.document.getRoot().getChild( 0 );
|
||||
|
||||
expect( firstViewChild.name ).not.to.equal( 'div' );
|
||||
expect( firstViewChild.hasClass( 'ck-mermaid__wrapper' ), 'has ck-mermaid__wrapper class' ).to.be.false;
|
||||
} );
|
||||
|
||||
it( 'adds a preview element', () => {
|
||||
setModelData( editor.model, '<mermaid source="foo"></mermaid>' );
|
||||
|
||||
const widgetChildren = [ ...editor.editing.view.document.getRoot().getChild( 0 ).getChildren() ];
|
||||
const previewView = widgetChildren.filter( item => item.name === 'div' && item.hasClass( 'ck-mermaid__preview' ) );
|
||||
|
||||
expect( previewView.length ).to.equal( 1 );
|
||||
} );
|
||||
|
||||
it( 'adds an editing element', () => {
|
||||
setModelData( editor.model, '<mermaid source="foo"></mermaid>' );
|
||||
|
||||
const widgetChildren = [ ...editor.editing.view.document.getRoot().getChild( 0 ).getChildren() ];
|
||||
const previewView = widgetChildren.filter(
|
||||
item => item.name === 'textarea' && item.hasClass( 'ck-mermaid__editing-view' )
|
||||
);
|
||||
|
||||
expect( previewView.length ).to.equal( 1 );
|
||||
} );
|
||||
} );
|
||||
} );
|
||||
} );
|
154
_regroup/ckeditor5-mermaid/tests/mermaidtoolbar.js
Normal file
154
_regroup/ckeditor5-mermaid/tests/mermaidtoolbar.js
Normal file
@ -0,0 +1,154 @@
|
||||
import { ClassicEditor as ClassicTestEditor } from '@ckeditor/ckeditor5-editor-classic';
|
||||
import { Essentials } from '@ckeditor/ckeditor5-essentials';
|
||||
import { Paragraph } from '@ckeditor/ckeditor5-paragraph';
|
||||
import { WidgetToolbarRepository } from '@ckeditor/ckeditor5-widget';
|
||||
import { _setModelData as setData } from '@ckeditor/ckeditor5-engine';
|
||||
|
||||
import Mermaid from '../src/mermaid.js';
|
||||
|
||||
/* global document */
|
||||
|
||||
describe( 'MermaidToolbar', () => {
|
||||
let editor, domElement, widgetToolbarRepository, balloon, toolbar, model;
|
||||
|
||||
beforeEach( () => {
|
||||
domElement = document.createElement( 'div' );
|
||||
document.body.appendChild( domElement );
|
||||
|
||||
return ClassicTestEditor.create( domElement, {
|
||||
plugins: [ Essentials, Paragraph, Mermaid ],
|
||||
mermaid: {
|
||||
toolbar: [ 'fake_button' ]
|
||||
}
|
||||
} ).then( newEditor => {
|
||||
editor = newEditor;
|
||||
model = newEditor.model;
|
||||
widgetToolbarRepository = editor.plugins.get( WidgetToolbarRepository );
|
||||
toolbar = widgetToolbarRepository._toolbarDefinitions.get( 'mermaidToolbar' ).view;
|
||||
balloon = editor.plugins.get( 'ContextualBalloon' );
|
||||
} );
|
||||
} );
|
||||
|
||||
afterEach( () => {
|
||||
domElement.remove();
|
||||
return editor.destroy();
|
||||
} );
|
||||
|
||||
describe( 'toolbar', () => {
|
||||
it( 'should be initialized with expected buttons', () => {
|
||||
editor.ui.focusTracker.isFocused = true;
|
||||
|
||||
setData( model, '<mermaid displayMode="split" source="">[]</mermaid>' );
|
||||
|
||||
expect( toolbar.items ).to.have.length( 5 );
|
||||
expect( toolbar.items.get( 0 ).label ).to.equal( 'Source view' );
|
||||
expect( toolbar.items.get( 1 ).label ).to.equal( 'Split view' );
|
||||
expect( toolbar.items.get( 2 ).label ).to.equal( 'Preview' );
|
||||
expect( toolbar.items.get( 4 ).label ).to.equal( 'Read more about Mermaid diagram syntax' );
|
||||
} );
|
||||
} );
|
||||
|
||||
describe( 'integration with the editor focus', () => {
|
||||
it( 'should show the toolbar when the editor gains focus and the mermaid widget is selected', () => {
|
||||
setData( model,
|
||||
'[<mermaid source="flowchart TB\nA --> B\nB --> C"></mermaid>]'
|
||||
);
|
||||
|
||||
expect( balloon.visibleView ).to.be.null;
|
||||
// @todo: remove me
|
||||
// expect( balloon.visibleView === null, 'balloon.visibleView === null' ).to.be.true;
|
||||
|
||||
editor.ui.focusTracker.isFocused = true;
|
||||
expect( balloon.visibleView ).to.equal( toolbar );
|
||||
// @todo: remove me
|
||||
// expect( balloon.visibleView === toolbar, 'balloon.visibleView === toolbar' ).to.be.true;
|
||||
} );
|
||||
|
||||
it( 'should hide the toolbar when the editor loses focus and the mermaid widget is selected', () => {
|
||||
setData( model,
|
||||
'[<mermaid source="flowchart TB\nA --> B\nB --> C"></mermaid>]'
|
||||
);
|
||||
|
||||
editor.ui.focusTracker.isFocused = true;
|
||||
expect( balloon.visibleView ).to.equal( toolbar );
|
||||
|
||||
editor.ui.focusTracker.isFocused = false;
|
||||
expect( balloon.visibleView ).to.be.null;
|
||||
} );
|
||||
} );
|
||||
|
||||
describe( 'integration with the editor selection', () => {
|
||||
beforeEach( () => {
|
||||
editor.ui.focusTracker.isFocused = true;
|
||||
} );
|
||||
|
||||
it( 'should show the toolbar on ui#update when the mermaid widget is selected', () => {
|
||||
setData( model,
|
||||
'<paragraph>[foo]</paragraph>' +
|
||||
'<mermaid source="flowchart TB\nA --> B\nB --> C"></mermaid>'
|
||||
);
|
||||
|
||||
expect( balloon.visibleView ).to.be.null;
|
||||
|
||||
editor.ui.fire( 'update' );
|
||||
|
||||
expect( balloon.visibleView ).to.be.null;
|
||||
|
||||
model.change( writer => {
|
||||
// Set selection to the [<mermaid source="flowchart TB\nA --> B\nB --> C"></mermaid>]
|
||||
writer.setSelection( model.document.getRoot().getChild( 1 ), 'on' );
|
||||
} );
|
||||
|
||||
expect( balloon.visibleView ).to.equal( toolbar );
|
||||
|
||||
// Make sure successive change does not throw, e.g. attempting
|
||||
// to insert the toolbar twice.
|
||||
editor.ui.fire( 'update' );
|
||||
|
||||
expect( balloon.visibleView ).to.equal( toolbar );
|
||||
} );
|
||||
|
||||
it( 'should hide the toolbar on ui#update if the mermaid widget is de–selected', () => {
|
||||
setData( model,
|
||||
'<paragraph>foo</paragraph>' +
|
||||
'[<mermaid source="flowchart TB\nA --> B\nB --> C"></mermaid>]'
|
||||
);
|
||||
expect( balloon.visibleView ).to.equal( toolbar );
|
||||
|
||||
model.change( writer => {
|
||||
// Select the <paragraph>[foo]</paragraph>
|
||||
writer.setSelection( model.document.getRoot().getChild( 0 ), 'in' );
|
||||
} );
|
||||
|
||||
expect( balloon.visibleView ).to.be.null;
|
||||
|
||||
// Make sure successive change does not throw, e.g. attempting
|
||||
// to remove the toolbar twice.
|
||||
editor.ui.fire( 'update' );
|
||||
|
||||
expect( balloon.visibleView ).to.be.null;
|
||||
} );
|
||||
|
||||
it( 'should not hide the toolbar on ui#update when the selection is being moved from one mermaid widget to another', () => {
|
||||
setData( model,
|
||||
'[<mermaid source="flowchart TB\nA --> B\nB --> C"></mermaid>]' +
|
||||
'<mermaid source="flowchart TB\nA --> B\nB --> C"></mermaid>'
|
||||
);
|
||||
|
||||
expect( balloon.visibleView ).to.equal( toolbar );
|
||||
|
||||
model.change( writer => {
|
||||
// Set selection to the second <mermaid></mermaid>
|
||||
writer.setSelection( model.document.selection.getSelectedElement().nextSibling, 'on' );
|
||||
} );
|
||||
|
||||
expect( balloon.visibleView ).to.equal( toolbar );
|
||||
|
||||
// Make sure successive change does not throw, e.g. attempting
|
||||
// to insert the toolbar twice.
|
||||
editor.ui.fire( 'update' );
|
||||
|
||||
expect( balloon.visibleView ).to.equal( toolbar );
|
||||
} );
|
||||
} );
|
||||
} );
|
88
_regroup/ckeditor5-mermaid/tests/mermaidui.js
Normal file
88
_regroup/ckeditor5-mermaid/tests/mermaidui.js
Normal file
@ -0,0 +1,88 @@
|
||||
import { ClassicEditor } from '@ckeditor/ckeditor5-editor-classic';
|
||||
import { _setModelData as setModelData } from '@ckeditor/ckeditor5-engine';
|
||||
|
||||
import Mermaid from '../src/mermaid.js';
|
||||
import MermaidUI from '../src/mermaidui.js';
|
||||
|
||||
/* global document */
|
||||
|
||||
describe( 'MermaidUI', () => {
|
||||
it( 'should be named', () => {
|
||||
expect( MermaidUI.pluginName ).to.equal( 'MermaidUI' );
|
||||
} );
|
||||
|
||||
describe( 'init()', () => {
|
||||
let domElement, editor;
|
||||
|
||||
beforeEach( async () => {
|
||||
domElement = document.createElement( 'div' );
|
||||
document.body.appendChild( domElement );
|
||||
|
||||
editor = await ClassicEditor.create( domElement, {
|
||||
plugins: [
|
||||
Mermaid
|
||||
]
|
||||
} );
|
||||
} );
|
||||
|
||||
afterEach( () => {
|
||||
domElement.remove();
|
||||
return editor.destroy();
|
||||
} );
|
||||
|
||||
it( 'should register the UI item', () => {
|
||||
expect( editor.ui.componentFactory.has( 'mermaid' ) ).to.equal( true );
|
||||
} );
|
||||
|
||||
it( 'has the base properties', () => {
|
||||
const button = editor.ui.componentFactory.create( 'mermaid' );
|
||||
|
||||
expect( button ).to.have.property( 'label', 'Insert Mermaid diagram' );
|
||||
expect( button ).to.have.property( 'icon' );
|
||||
expect( button ).to.have.property( 'tooltip', true );
|
||||
} );
|
||||
|
||||
describe( 'UI components', () => {
|
||||
for ( const buttonName of [
|
||||
'mermaidPreview',
|
||||
'mermaidSourceView',
|
||||
'mermaidSplitView',
|
||||
'mermaidInfo'
|
||||
] ) {
|
||||
it( `should register the ${ buttonName } button`, () => {
|
||||
expect( editor.ui.componentFactory.has( buttonName ) ).to.equal( true );
|
||||
} );
|
||||
|
||||
it( `should add the base properties for ${ buttonName } button`, () => {
|
||||
const button = editor.ui.componentFactory.create( buttonName );
|
||||
|
||||
expect( button ).to.have.property( 'label' );
|
||||
expect( button ).to.have.property( 'icon' );
|
||||
expect( button ).to.have.property( 'tooltip', true );
|
||||
} );
|
||||
}
|
||||
} );
|
||||
|
||||
it( 'should set focus inside textarea of a newly created mermaid', () => {
|
||||
const button = editor.ui.componentFactory.create( 'mermaid' );
|
||||
|
||||
button.fire( 'execute' );
|
||||
|
||||
expect( document.activeElement.tagName ).to.equal( 'TEXTAREA' );
|
||||
} );
|
||||
|
||||
it( 'should not crash if the button is fired inside model.change()', () => {
|
||||
const button = editor.ui.componentFactory.create( 'mermaid' );
|
||||
|
||||
setModelData( editor.model, '[]' );
|
||||
|
||||
editor.model.change( () => {
|
||||
button.fire( 'execute' );
|
||||
} );
|
||||
// As the conversion is to be executed after the model.change(), we don't have access to the fully prepared view and
|
||||
// despite that, we should still successfully add mermaid widget to the editor, not requiring the selection change
|
||||
// to the inside of the nonexisting textarea element.
|
||||
} );
|
||||
} );
|
||||
} );
|
||||
|
1
_regroup/ckeditor5-mermaid/theme/icons/info.svg
Normal file
1
_regroup/ckeditor5-mermaid/theme/icons/info.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M8 1.219C4.254 1.219 1.219 4.28 1.219 8A6.78 6.78 0 0 0 8 14.781c3.719 0 6.781-3.035 6.781-6.781 0-3.719-3.062-6.781-6.781-6.781Zm0 12.25A5.45 5.45 0 0 1 2.531 8 5.467 5.467 0 0 1 8 2.531c3.008 0 5.469 2.461 5.469 5.469A5.467 5.467 0 0 1 8 13.469Zm0-9.242c-.656 0-1.148.52-1.148 1.148 0 .656.492 1.148 1.148 1.148.629 0 1.148-.492 1.148-1.148 0-.629-.52-1.148-1.148-1.148Zm1.531 6.945v-.656a.353.353 0 0 0-.328-.329h-.328V7.454a.353.353 0 0 0-.328-.328h-1.75a.332.332 0 0 0-.328.328v.656c0 .192.136.329.328.329h.328v1.75h-.328a.333.333 0 0 0-.328.328v.656c0 .191.136.328.328.328h2.406a.332.332 0 0 0 .328-.328Z"/></svg>
|
After Width: | Height: | Size: 712 B |
1
_regroup/ckeditor5-mermaid/theme/icons/insert.svg
Normal file
1
_regroup/ckeditor5-mermaid/theme/icons/insert.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="m8 11.5 3.004-3.004 3.003 3.004-3.003 3.004L8 11.5Z"/><path fill-rule="evenodd" clip-rule="evenodd" d="M9.504 2.863v3h3v-3h-3Zm-1 4h5v-5h-5v5Z"/><path fill-rule="evenodd" clip-rule="evenodd" d="M11.004 14.504 8 11.5l2.504-2.504V6.07h1v2.926l2.503 2.504-3.003 3.004ZM9.414 11.5l1.59-1.59 1.59 1.59-1.59 1.59-1.59-1.59ZM6.837 4.999h2.625v-1h-2.57a2.5 2.5 0 1 0-2.974 2.814V9h-2v5h5V9h-2V6.813c.934-.19 1.68-.9 1.919-1.814Zm-3.919-.636a1.5 1.5 0 1 1 3 0 1.5 1.5 0 0 1-3 0Zm0 5.637v3h3v-3h-3Z"/></svg>
|
After Width: | Height: | Size: 590 B |
1
_regroup/ckeditor5-mermaid/theme/icons/preview-mode.svg
Normal file
1
_regroup/ckeditor5-mermaid/theme/icons/preview-mode.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M13.8 11.609V13.4a.4.4 0 0 1-.4.4H2.6a.4.4 0 0 1-.4-.4v-1.791H1V13.4A1.6 1.6 0 0 0 2.6 15h10.8a1.6 1.6 0 0 0 1.6-1.6v-1.791h-1.2ZM13.8 4.997H15V2.6A1.6 1.6 0 0 0 13.4 1H2.6A1.6 1.6 0 0 0 1 2.6v2.397h1.2V2.6c0-.22.18-.4.4-.4h10.8c.22 0 .4.18.4.4v2.397Z"/><path d="M8 11.095c-1.92 0-3.837-.919-5.749-2.757L2 8.095l.251-.242c3.815-3.677 7.683-3.677 11.498 0l.251.242-.251.243C11.84 10.176 9.925 11.095 8 11.096Zm-5.02-3c3.375 3.1 6.665 3.1 10.04 0-3.375-3.1-6.665-3.095-10.04 0Z"/><path fill-rule="evenodd" clip-rule="evenodd" d="M13.514 8.095c-3.676 3.543-7.352 3.543-11.028 0 3.676-3.542 7.352-3.542 11.028 0Zm-3.586 0A1.926 1.926 0 0 1 8 10.02a1.926 1.926 0 0 1-1.928-1.924c0-1.062.863-1.924 1.928-1.924s1.928.862 1.928 1.924Z"/><path d="M8 8.861a.767.767 0 1 0 .002-1.533A.767.767 0 0 0 8 8.86Z"/></svg>
|
After Width: | Height: | Size: 897 B |
1
_regroup/ckeditor5-mermaid/theme/icons/source-mode.svg
Normal file
1
_regroup/ckeditor5-mermaid/theme/icons/source-mode.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M11.609 2.2H13.4c.22 0 .4.18.4.4v10.8a.4.4 0 0 1-.4.4h-1.791V15H13.4a1.6 1.6 0 0 0 1.6-1.6V2.6A1.6 1.6 0 0 0 13.4 1h-1.791v1.2ZM4.997 2.2V1H2.6A1.6 1.6 0 0 0 1 2.6v10.8A1.6 1.6 0 0 0 2.6 15h2.397v-1.2H2.6a.4.4 0 0 1-.4-.4V2.6c0-.22.18-.4.4-.4h2.397Z"/><path d="M3.511 4.778a.75.75 0 0 1 .75-.75h3.697a.75.75 0 1 1 0 1.5H4.26a.75.75 0 0 1-.75-.75ZM6.595 7.629a.75.75 0 0 1 .75-.75h3.588a.75.75 0 0 1 0 1.5H7.345a.75.75 0 0 1-.75-.75ZM6.595 10.48a.75.75 0 0 1 .75-.75h1.143a.75.75 0 1 1 0 1.5H7.345a.75.75 0 0 1-.75-.75ZM3.511 7.629a.75.75 0 0 1 .75-.75h.983a.75.75 0 1 1 0 1.5h-.983a.75.75 0 0 1-.75-.75ZM3.511 10.48a.75.75 0 0 1 .75-.75h.983a.75.75 0 1 1 0 1.5h-.983a.75.75 0 0 1-.75-.75Z"/></svg>
|
After Width: | Height: | Size: 790 B |
1
_regroup/ckeditor5-mermaid/theme/icons/split-mode.svg
Normal file
1
_regroup/ckeditor5-mermaid/theme/icons/split-mode.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M13.8 3.076H8.588V13.8H13.4a.4.4 0 0 0 .4-.4V3.076Zm-6.412 0H2.2V13.4c0 .22.18.4.4.4h4.788V3.076ZM2.6 1A1.6 1.6 0 0 0 1 2.6v10.8A1.6 1.6 0 0 0 2.6 15h10.8a1.6 1.6 0 0 0 1.6-1.6V2.6A1.6 1.6 0 0 0 13.4 1H2.6Z"/></svg>
|
After Width: | Height: | Size: 348 B |
61
_regroup/ckeditor5-mermaid/theme/mermaid.css
Normal file
61
_regroup/ckeditor5-mermaid/theme/mermaid.css
Normal file
@ -0,0 +1,61 @@
|
||||
/* Mermaid wrapper */
|
||||
.ck-mermaid__wrapper {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
/* Editing
|
||||
Assigned to textarea
|
||||
*/
|
||||
.ck-mermaid__wrapper .ck-mermaid__editing-view {
|
||||
padding: 16px; /* just like pre element */
|
||||
background-color: hsla(0, 0%, 78%, 0.3); /* just like pre element */
|
||||
min-height: 200px;
|
||||
border: 0;
|
||||
resize: vertical;
|
||||
font-size: 12px; /* just like pre element */
|
||||
outline: 0; /* just like pre element */
|
||||
}
|
||||
|
||||
/* Preview */
|
||||
.ck-mermaid__preview {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
/* Preview error state */
|
||||
[id^="dmermaid-"] {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
[id^="dmermaid-"] svg {
|
||||
display: block;
|
||||
width: 60%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
/* Source mode */
|
||||
.ck-mermaid__source-mode .ck-mermaid__editing-view {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.ck-mermaid__source-mode .ck-mermaid__preview {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Split mode */
|
||||
.ck-mermaid__split-mode .ck-mermaid__editing-view {
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
.ck-mermaid__split-mode .ck-mermaid__preview {
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
/* Preview mode */
|
||||
.ck-mermaid__preview-mode .ck-mermaid__editing-view {
|
||||
display: none;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user