@gravity-ui/markdown-editor 14.4.0 → 14.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/cjs/bundle/Editor.js +1 -0
- package/build/cjs/bundle/config/markup.d.ts +41 -17
- package/build/cjs/bundle/config/markup.js +413 -308
- package/build/cjs/bundle/config/wysiwyg.d.ts +29 -18
- package/build/cjs/bundle/config/wysiwyg.js +526 -310
- package/build/cjs/bundle/sticky/sticky.css +1 -1
- package/build/cjs/bundle/types.d.ts +2 -0
- package/build/cjs/extensions/behavior/Clipboard/utils.d.ts +1 -0
- package/build/cjs/extensions/behavior/Clipboard/utils.js +1 -0
- package/build/cjs/extensions/markdown/CodeBlock/handle-paste.js +5 -17
- package/build/cjs/extensions/yfm/Checkbox/CheckboxSpecs/const.d.ts +7 -0
- package/build/cjs/extensions/yfm/Checkbox/CheckboxSpecs/const.js +8 -1
- package/build/cjs/extensions/yfm/Checkbox/CheckboxSpecs/index.d.ts +1 -1
- package/build/cjs/extensions/yfm/Checkbox/CheckboxSpecs/index.js +2 -1
- package/build/cjs/extensions/yfm/Checkbox/CheckboxSpecs/schema.d.ts +1 -1
- package/build/cjs/extensions/yfm/Checkbox/CheckboxSpecs/schema.js +7 -7
- package/build/cjs/extensions/yfm/Checkbox/CheckboxSpecs/serializer.d.ts +1 -1
- package/build/cjs/extensions/yfm/Checkbox/CheckboxSpecs/serializer.js +2 -2
- package/build/cjs/extensions/yfm/Checkbox/const.d.ts +1 -1
- package/build/cjs/extensions/yfm/Checkbox/const.js +2 -1
- package/build/cjs/extensions/yfm/Checkbox/index.d.ts +2 -2
- package/build/cjs/extensions/yfm/Checkbox/index.js +4 -38
- package/build/cjs/extensions/yfm/Checkbox/nodeviews.d.ts +16 -0
- package/build/cjs/extensions/yfm/Checkbox/nodeviews.js +56 -0
- package/build/cjs/extensions/yfm/YfmFile/YfmFileSpecs/const.d.ts +12 -0
- package/build/cjs/extensions/yfm/YfmFile/YfmFileSpecs/const.js +21 -2
- package/build/cjs/extensions/yfm/YfmFile/YfmFileSpecs/index.d.ts +8 -1
- package/build/cjs/extensions/yfm/YfmFile/YfmFileSpecs/index.js +29 -5
- package/build/cjs/markup/codemirror/create.d.ts +1 -0
- package/build/cjs/markup/codemirror/create.js +41 -4
- package/build/cjs/markup/codemirror/html-to-markdown/converters.d.ts +111 -0
- package/build/cjs/markup/codemirror/html-to-markdown/converters.js +214 -0
- package/build/cjs/markup/codemirror/html-to-markdown/handlers.d.ts +104 -0
- package/build/cjs/markup/codemirror/html-to-markdown/handlers.js +233 -0
- package/build/cjs/markup/codemirror/html-to-markdown/helpers.d.ts +1 -0
- package/build/cjs/markup/codemirror/html-to-markdown/helpers.js +21 -0
- package/build/cjs/markup/commands/inline.js +18 -8
- package/build/cjs/utils/clipboard.d.ts +14 -0
- package/build/cjs/utils/clipboard.js +36 -1
- package/build/cjs/version.js +1 -1
- package/build/esm/bundle/Editor.js +1 -0
- package/build/esm/bundle/config/markup.d.ts +41 -17
- package/build/esm/bundle/config/markup.js +411 -307
- package/build/esm/bundle/config/wysiwyg.d.ts +29 -18
- package/build/esm/bundle/config/wysiwyg.js +499 -284
- package/build/esm/bundle/sticky/sticky.css +1 -1
- package/build/esm/bundle/types.d.ts +2 -0
- package/build/esm/extensions/behavior/Clipboard/utils.d.ts +1 -0
- package/build/esm/extensions/behavior/Clipboard/utils.js +1 -0
- package/build/esm/extensions/markdown/CodeBlock/handle-paste.js +2 -14
- package/build/esm/extensions/yfm/Checkbox/CheckboxSpecs/const.d.ts +7 -0
- package/build/esm/extensions/yfm/Checkbox/CheckboxSpecs/const.js +7 -0
- package/build/esm/extensions/yfm/Checkbox/CheckboxSpecs/index.d.ts +1 -1
- package/build/esm/extensions/yfm/Checkbox/CheckboxSpecs/index.js +1 -1
- package/build/esm/extensions/yfm/Checkbox/CheckboxSpecs/schema.d.ts +1 -1
- package/build/esm/extensions/yfm/Checkbox/CheckboxSpecs/schema.js +7 -7
- package/build/esm/extensions/yfm/Checkbox/CheckboxSpecs/serializer.d.ts +1 -1
- package/build/esm/extensions/yfm/Checkbox/CheckboxSpecs/serializer.js +2 -2
- package/build/esm/extensions/yfm/Checkbox/const.d.ts +1 -1
- package/build/esm/extensions/yfm/Checkbox/const.js +1 -1
- package/build/esm/extensions/yfm/Checkbox/index.d.ts +2 -2
- package/build/esm/extensions/yfm/Checkbox/index.js +3 -38
- package/build/esm/extensions/yfm/Checkbox/nodeviews.d.ts +16 -0
- package/build/esm/extensions/yfm/Checkbox/nodeviews.js +52 -0
- package/build/esm/extensions/yfm/YfmFile/YfmFileSpecs/const.d.ts +12 -0
- package/build/esm/extensions/yfm/YfmFile/YfmFileSpecs/const.js +21 -2
- package/build/esm/extensions/yfm/YfmFile/YfmFileSpecs/index.d.ts +8 -1
- package/build/esm/extensions/yfm/YfmFile/YfmFileSpecs/index.js +29 -6
- package/build/esm/markup/codemirror/create.d.ts +1 -0
- package/build/esm/markup/codemirror/create.js +40 -3
- package/build/esm/markup/codemirror/html-to-markdown/converters.d.ts +111 -0
- package/build/esm/markup/codemirror/html-to-markdown/converters.js +210 -0
- package/build/esm/markup/codemirror/html-to-markdown/handlers.d.ts +104 -0
- package/build/esm/markup/codemirror/html-to-markdown/handlers.js +215 -0
- package/build/esm/markup/codemirror/html-to-markdown/helpers.d.ts +1 -0
- package/build/esm/markup/codemirror/html-to-markdown/helpers.js +17 -0
- package/build/esm/markup/commands/inline.js +18 -8
- package/build/esm/utils/clipboard.d.ts +14 -0
- package/build/esm/utils/clipboard.js +32 -0
- package/build/esm/version.js +1 -1
- package/build/styles.css +1 -1
- package/package.json +9 -7
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
position: absolute;
|
|
16
16
|
inset: var(--g-md-toolbar-sticky-inset, -4px);
|
|
17
17
|
content: "";
|
|
18
|
-
border: 1px solid var(--g-color-line-generic-solid);
|
|
18
|
+
border: var(--g-md-toolbar-sticky-border, 1px solid var(--g-color-line-generic-solid));
|
|
19
19
|
border-radius: 4px;
|
|
20
20
|
background-color: var(--g-color-base-background);
|
|
21
21
|
}
|
|
@@ -109,6 +109,8 @@ export declare type MarkdownEditorMarkupConfig = {
|
|
|
109
109
|
keymaps?: CreateCodemirrorParams['keymaps'];
|
|
110
110
|
/** Overrides the default placeholder content. */
|
|
111
111
|
placeholder?: CreateCodemirrorParams['placeholder'];
|
|
112
|
+
/** Enable HTML parsing when pasting content. */
|
|
113
|
+
parseHtmlOnPaste?: boolean;
|
|
112
114
|
/**
|
|
113
115
|
* Additional language data for markdown language in codemirror.
|
|
114
116
|
* Can be used to configure additional autocompletions and others.
|
|
@@ -4,6 +4,7 @@ export var DataTransferType;
|
|
|
4
4
|
DataTransferType["Text"] = "text/plain";
|
|
5
5
|
DataTransferType["Html"] = "text/html";
|
|
6
6
|
DataTransferType["Yfm"] = "text/yfm";
|
|
7
|
+
DataTransferType["Rtf"] = "text/rtf";
|
|
7
8
|
DataTransferType["UriList"] = "text/uri-list";
|
|
8
9
|
DataTransferType["VSCodeData"] = "vscode-editor-data";
|
|
9
10
|
DataTransferType["Files"] = "Files";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { DataTransferType } from '
|
|
1
|
+
import { DataTransferType, isVSCode, tryParseVSCodeData } from '../../../utils/clipboard';
|
|
2
2
|
import { cbType, codeBlockLangAttr } from './const';
|
|
3
3
|
export const handlePaste = (view, e) => {
|
|
4
4
|
if (!e.clipboardData || view.state.selection.$from.parent.type.spec.code)
|
|
@@ -20,7 +20,7 @@ function getCodeData(data) {
|
|
|
20
20
|
let mode;
|
|
21
21
|
if (isVSCode(data)) {
|
|
22
22
|
editor = 'vscode';
|
|
23
|
-
mode = (_a =
|
|
23
|
+
mode = (_a = tryParseVSCodeData(data)) === null || _a === void 0 ? void 0 : _a.mode;
|
|
24
24
|
}
|
|
25
25
|
else
|
|
26
26
|
return null;
|
|
@@ -28,15 +28,3 @@ function getCodeData(data) {
|
|
|
28
28
|
}
|
|
29
29
|
return null;
|
|
30
30
|
}
|
|
31
|
-
function isVSCode(data) {
|
|
32
|
-
return data.types.includes(DataTransferType.VSCodeData);
|
|
33
|
-
}
|
|
34
|
-
function tryCatch(fn) {
|
|
35
|
-
try {
|
|
36
|
-
return fn();
|
|
37
|
-
}
|
|
38
|
-
catch (e) {
|
|
39
|
-
console.error(e);
|
|
40
|
-
}
|
|
41
|
-
return undefined;
|
|
42
|
-
}
|
|
@@ -3,5 +3,12 @@ export declare enum CheckboxNode {
|
|
|
3
3
|
Input = "checkbox_input",
|
|
4
4
|
Label = "checkbox_label"
|
|
5
5
|
}
|
|
6
|
+
export declare const CheckboxAttr: {
|
|
7
|
+
readonly Class: "class";
|
|
8
|
+
readonly Type: "type";
|
|
9
|
+
readonly Id: "id";
|
|
10
|
+
readonly Checked: "checked";
|
|
11
|
+
readonly For: "for";
|
|
12
|
+
};
|
|
6
13
|
export declare const idPrefix = "yfm-editor-checkbox";
|
|
7
14
|
export declare const b: import("@bem-react/classname").ClassNameFormatter;
|
|
@@ -5,5 +5,12 @@ export var CheckboxNode;
|
|
|
5
5
|
CheckboxNode["Input"] = "checkbox_input";
|
|
6
6
|
CheckboxNode["Label"] = "checkbox_label";
|
|
7
7
|
})(CheckboxNode || (CheckboxNode = {}));
|
|
8
|
+
export const CheckboxAttr = {
|
|
9
|
+
Class: 'class',
|
|
10
|
+
Type: 'type',
|
|
11
|
+
Id: 'id',
|
|
12
|
+
Checked: 'checked',
|
|
13
|
+
For: 'for',
|
|
14
|
+
};
|
|
8
15
|
export const idPrefix = 'yfm-editor-checkbox';
|
|
9
16
|
export const b = cn('checkbox');
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { NodeSpec } from 'prosemirror-model';
|
|
2
2
|
import type { ExtensionAuto, ExtensionNodeSpec } from '../../../../core';
|
|
3
|
-
export { CheckboxNode } from './const';
|
|
3
|
+
export { CheckboxAttr, CheckboxNode } from './const';
|
|
4
4
|
export declare const checkboxType: (schema: import("prosemirror-model").Schema<any, any>) => import("prosemirror-model").NodeType;
|
|
5
5
|
export declare const checkboxLabelType: (schema: import("prosemirror-model").Schema<any, any>) => import("prosemirror-model").NodeType;
|
|
6
6
|
export declare const checkboxInputType: (schema: import("prosemirror-model").Schema<any, any>) => import("prosemirror-model").NodeType;
|
|
@@ -4,7 +4,7 @@ import { CheckboxNode, b, idPrefix } from './const';
|
|
|
4
4
|
import { parserTokens } from './parser';
|
|
5
5
|
import { getSchemaSpecs } from './schema';
|
|
6
6
|
import { serializerTokens } from './serializer';
|
|
7
|
-
export { CheckboxNode } from './const';
|
|
7
|
+
export { CheckboxAttr, CheckboxNode } from './const';
|
|
8
8
|
export const checkboxType = nodeTypeFactory(CheckboxNode.Checkbox);
|
|
9
9
|
export const checkboxLabelType = nodeTypeFactory(CheckboxNode.Label);
|
|
10
10
|
export const checkboxInputType = nodeTypeFactory(CheckboxNode.Input);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { NodeSpec } from 'prosemirror-model';
|
|
2
2
|
import { PlaceholderOptions } from '../../../../utils/placeholder';
|
|
3
|
-
import { CheckboxNode } from '
|
|
3
|
+
import { CheckboxNode } from './const';
|
|
4
4
|
import type { CheckboxSpecsOptions } from './index';
|
|
5
5
|
export declare const getSchemaSpecs: (opts?: Pick<CheckboxSpecsOptions, 'checkboxLabelPlaceholder'>, placeholder?: PlaceholderOptions) => Record<CheckboxNode, NodeSpec>;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CheckboxNode, b } from '
|
|
1
|
+
import { CheckboxAttr, CheckboxNode, b } from './const';
|
|
2
2
|
const DEFAULT_LABEL_PLACEHOLDER = 'Checkbox';
|
|
3
3
|
export const getSchemaSpecs = (opts, placeholder) => {
|
|
4
4
|
var _a, _b;
|
|
@@ -10,7 +10,7 @@ export const getSchemaSpecs = (opts, placeholder) => {
|
|
|
10
10
|
allowSelection: false,
|
|
11
11
|
parseDOM: [],
|
|
12
12
|
attrs: {
|
|
13
|
-
|
|
13
|
+
[CheckboxAttr.Class]: { default: b() },
|
|
14
14
|
},
|
|
15
15
|
toDOM(node) {
|
|
16
16
|
return ['div', node.attrs, 0];
|
|
@@ -21,9 +21,9 @@ export const getSchemaSpecs = (opts, placeholder) => {
|
|
|
21
21
|
group: 'block',
|
|
22
22
|
parseDOM: [],
|
|
23
23
|
attrs: {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
[CheckboxAttr.Type]: { default: 'checkbox' },
|
|
25
|
+
[CheckboxAttr.Id]: { default: null },
|
|
26
|
+
[CheckboxAttr.Checked]: { default: null },
|
|
27
27
|
},
|
|
28
28
|
toDOM(node) {
|
|
29
29
|
return ['div', node.attrs];
|
|
@@ -39,12 +39,12 @@ export const getSchemaSpecs = (opts, placeholder) => {
|
|
|
39
39
|
{
|
|
40
40
|
tag: `span[class="${b('label')}"]`,
|
|
41
41
|
getAttrs: (node) => ({
|
|
42
|
-
|
|
42
|
+
[CheckboxAttr.For]: node.getAttribute(CheckboxAttr.For) || '',
|
|
43
43
|
}),
|
|
44
44
|
},
|
|
45
45
|
],
|
|
46
46
|
attrs: {
|
|
47
|
-
|
|
47
|
+
[CheckboxAttr.For]: { default: null },
|
|
48
48
|
},
|
|
49
49
|
escapeText: false,
|
|
50
50
|
placeholder: {
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { getPlaceholderContent } from '../../../../utils/placeholder';
|
|
2
|
-
import { CheckboxNode } from '
|
|
2
|
+
import { CheckboxAttr, CheckboxNode } from './const';
|
|
3
3
|
export const serializerTokens = {
|
|
4
4
|
[CheckboxNode.Checkbox]: (state, node) => {
|
|
5
5
|
state.renderInline(node);
|
|
6
6
|
state.closeBlock(node);
|
|
7
7
|
},
|
|
8
8
|
[CheckboxNode.Input]: (state, node) => {
|
|
9
|
-
const checked = node.attrs.
|
|
9
|
+
const checked = node.attrs[CheckboxAttr.Checked] === 'true';
|
|
10
10
|
state.write(`[${checked ? 'X' : ' '}] `);
|
|
11
11
|
},
|
|
12
12
|
[CheckboxNode.Label]: (state, node, _, idx) => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export { CheckboxNode, b } from './CheckboxSpecs/const';
|
|
1
|
+
export { CheckboxAttr, CheckboxNode, b } from './CheckboxSpecs/const';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export { CheckboxNode, b } from './CheckboxSpecs/const';
|
|
1
|
+
export { CheckboxAttr, CheckboxNode, b } from './CheckboxSpecs/const';
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { Action, ExtensionAuto } from '../../../core';
|
|
2
|
-
import { CheckboxSpecsOptions } from './CheckboxSpecs';
|
|
2
|
+
import { type CheckboxSpecsOptions } from './CheckboxSpecs';
|
|
3
3
|
import './index.css';
|
|
4
4
|
declare const checkboxAction = "addCheckbox";
|
|
5
|
-
export { CheckboxNode, checkboxType, checkboxLabelType, checkboxInputType } from './CheckboxSpecs';
|
|
5
|
+
export { CheckboxAttr, CheckboxNode, checkboxType, checkboxLabelType, checkboxInputType, } from './CheckboxSpecs';
|
|
6
6
|
export declare type CheckboxOptions = Pick<CheckboxSpecsOptions, 'checkboxLabelPlaceholder'> & {};
|
|
7
7
|
export declare const Checkbox: ExtensionAuto<CheckboxOptions>;
|
|
8
8
|
declare global {
|
|
@@ -1,49 +1,14 @@
|
|
|
1
|
-
import { replaceParentNodeOfType } from 'prosemirror-utils';
|
|
2
1
|
import { nodeInputRule } from '../../../utils/inputrules';
|
|
3
|
-
import { pType } from '../../base/BaseSchema';
|
|
4
2
|
import { CheckboxSpecs } from './CheckboxSpecs';
|
|
5
3
|
import { addCheckbox } from './actions';
|
|
6
|
-
import {
|
|
4
|
+
import { CheckboxInputView } from './nodeviews';
|
|
7
5
|
import { keymapPlugin } from './plugin';
|
|
8
6
|
import { checkboxInputType, checkboxType } from './utils';
|
|
9
7
|
import './index.css';
|
|
10
8
|
const checkboxAction = 'addCheckbox';
|
|
11
|
-
export { CheckboxNode, checkboxType, checkboxLabelType, checkboxInputType } from './CheckboxSpecs';
|
|
9
|
+
export { CheckboxAttr, CheckboxNode, checkboxType, checkboxLabelType, checkboxInputType, } from './CheckboxSpecs';
|
|
12
10
|
export const Checkbox = (builder, opts) => {
|
|
13
|
-
builder.use(CheckboxSpecs, Object.assign(Object.assign({}, opts), { inputView: () =>
|
|
14
|
-
const dom = document.createElement('input');
|
|
15
|
-
for (const attr in node.attrs) {
|
|
16
|
-
if (node.attrs[attr])
|
|
17
|
-
dom.setAttribute(attr, node.attrs[attr]);
|
|
18
|
-
}
|
|
19
|
-
dom.setAttribute('class', b('input'));
|
|
20
|
-
dom.addEventListener('click', (e) => {
|
|
21
|
-
const elem = e.target;
|
|
22
|
-
const checkedAttr = elem.getAttribute('checked');
|
|
23
|
-
const checked = checkedAttr ? '' : 'true';
|
|
24
|
-
const pos = getPos();
|
|
25
|
-
if (pos !== undefined) {
|
|
26
|
-
view.dispatch(view.state.tr.setNodeMarkup(pos, undefined, Object.assign(Object.assign({}, node.attrs), { checked })));
|
|
27
|
-
}
|
|
28
|
-
elem.setAttribute('checked', checked);
|
|
29
|
-
});
|
|
30
|
-
return {
|
|
31
|
-
dom,
|
|
32
|
-
ignoreMutation: () => true,
|
|
33
|
-
update: () => true,
|
|
34
|
-
destroy() {
|
|
35
|
-
const pos = getPos();
|
|
36
|
-
if (pos !== undefined) {
|
|
37
|
-
const resolved = view.state.doc.resolve(pos);
|
|
38
|
-
if (resolved.parent.type.name === CheckboxNode.Checkbox &&
|
|
39
|
-
resolved.parent.lastChild) {
|
|
40
|
-
view.dispatch(replaceParentNodeOfType(resolved.parent.type, pType(view.state.schema).create(resolved.parent.lastChild.content))(view.state.tr));
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
dom.remove();
|
|
44
|
-
},
|
|
45
|
-
};
|
|
46
|
-
} }));
|
|
11
|
+
builder.use(CheckboxSpecs, Object.assign(Object.assign({}, opts), { inputView: () => CheckboxInputView.create }));
|
|
47
12
|
builder
|
|
48
13
|
.addPlugin(keymapPlugin, builder.Priority.High)
|
|
49
14
|
.addAction(checkboxAction, () => addCheckbox())
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { Node } from 'prosemirror-model';
|
|
2
|
+
import type { NodeView, NodeViewConstructor } from 'prosemirror-view';
|
|
3
|
+
export declare class CheckboxInputView implements NodeView {
|
|
4
|
+
static create: NodeViewConstructor;
|
|
5
|
+
dom: HTMLInputElement;
|
|
6
|
+
private _node;
|
|
7
|
+
private _view;
|
|
8
|
+
private _getPos;
|
|
9
|
+
private constructor();
|
|
10
|
+
ignoreMutation(): boolean;
|
|
11
|
+
update(node: Node): boolean;
|
|
12
|
+
destroy(): void;
|
|
13
|
+
private _createDomElem;
|
|
14
|
+
private _applyNodeAttrsToDomElem;
|
|
15
|
+
private _onInputClick;
|
|
16
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
var _a;
|
|
2
|
+
import { CheckboxAttr, b } from './const';
|
|
3
|
+
export class CheckboxInputView {
|
|
4
|
+
constructor(node, view, getPos) {
|
|
5
|
+
this._onInputClick = (event) => {
|
|
6
|
+
if (event.target instanceof HTMLInputElement) {
|
|
7
|
+
const { checked } = event.target;
|
|
8
|
+
const pos = this._getPos();
|
|
9
|
+
if (pos !== undefined)
|
|
10
|
+
this._view.dispatch(this._view.state.tr.setNodeAttribute(pos, CheckboxAttr.Checked, checked ? 'true' : null));
|
|
11
|
+
}
|
|
12
|
+
this._view.focus();
|
|
13
|
+
};
|
|
14
|
+
this._node = node;
|
|
15
|
+
this._view = view;
|
|
16
|
+
this._getPos = getPos;
|
|
17
|
+
this.dom = this._createDomElem();
|
|
18
|
+
this._applyNodeAttrsToDomElem();
|
|
19
|
+
}
|
|
20
|
+
ignoreMutation() {
|
|
21
|
+
return true;
|
|
22
|
+
}
|
|
23
|
+
update(node) {
|
|
24
|
+
if (node.type !== this._node.type)
|
|
25
|
+
return false;
|
|
26
|
+
this._node = node;
|
|
27
|
+
this._applyNodeAttrsToDomElem();
|
|
28
|
+
return true;
|
|
29
|
+
}
|
|
30
|
+
destroy() {
|
|
31
|
+
this.dom.removeEventListener('click', this._onInputClick);
|
|
32
|
+
}
|
|
33
|
+
_createDomElem() {
|
|
34
|
+
const dom = document.createElement('input');
|
|
35
|
+
dom.setAttribute('class', b('input'));
|
|
36
|
+
dom.addEventListener('click', this._onInputClick);
|
|
37
|
+
return dom;
|
|
38
|
+
}
|
|
39
|
+
_applyNodeAttrsToDomElem() {
|
|
40
|
+
const { dom, _node: node } = this;
|
|
41
|
+
for (const [key, value] of Object.entries(node.attrs)) {
|
|
42
|
+
if (value)
|
|
43
|
+
dom.setAttribute(key, value);
|
|
44
|
+
else
|
|
45
|
+
dom.removeAttribute(key);
|
|
46
|
+
}
|
|
47
|
+
const checked = node.attrs[CheckboxAttr.Checked] === 'true';
|
|
48
|
+
this.dom.checked = checked;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
_a = CheckboxInputView;
|
|
52
|
+
CheckboxInputView.create = (node, view, getPos) => new _a(node, view, getPos);
|
|
@@ -1,5 +1,17 @@
|
|
|
1
|
+
import { FileHtmlAttr } from '@diplodoc/file-extension';
|
|
1
2
|
import type { AttributeSpec } from 'prosemirror-model';
|
|
2
3
|
export declare const yfmFileNodeName = "yfm_file";
|
|
4
|
+
export declare const YfmFileAttr: {
|
|
5
|
+
readonly Markup: "data-markup";
|
|
6
|
+
readonly Name: FileHtmlAttr.Download;
|
|
7
|
+
readonly Link: FileHtmlAttr.Href;
|
|
8
|
+
readonly ReferrerPolicy: FileHtmlAttr.ReferrerPolicy;
|
|
9
|
+
readonly Rel: FileHtmlAttr.Rel;
|
|
10
|
+
readonly Target: FileHtmlAttr.Target;
|
|
11
|
+
readonly Type: FileHtmlAttr.Type;
|
|
12
|
+
readonly Lang: FileHtmlAttr.HrefLang;
|
|
13
|
+
};
|
|
14
|
+
export declare const YFM_FILE_DIRECTIVE_ATTRS: readonly string[];
|
|
3
15
|
export declare const KNOWN_ATTRS: readonly string[];
|
|
4
16
|
export declare const REQUIRED_ATTRS: string[];
|
|
5
17
|
export declare const fileNodeAttrsSpec: Record<string, AttributeSpec>;
|
|
@@ -1,5 +1,22 @@
|
|
|
1
|
-
import { FILE_KNOWN_ATTRS, FILE_REQUIRED_ATTRS, FILE_TOKEN, FILE_TO_LINK_ATTRS_MAP, } from '@diplodoc/file-extension';
|
|
1
|
+
import { FILE_KNOWN_ATTRS, FILE_REQUIRED_ATTRS, FILE_TOKEN, FILE_TO_LINK_ATTRS_MAP, FileHtmlAttr, } from '@diplodoc/file-extension';
|
|
2
2
|
export const yfmFileNodeName = FILE_TOKEN;
|
|
3
|
+
export const YfmFileAttr = {
|
|
4
|
+
Markup: 'data-markup',
|
|
5
|
+
Name: FileHtmlAttr.Download,
|
|
6
|
+
Link: FileHtmlAttr.Href,
|
|
7
|
+
ReferrerPolicy: FileHtmlAttr.ReferrerPolicy,
|
|
8
|
+
Rel: FileHtmlAttr.Rel,
|
|
9
|
+
Target: FileHtmlAttr.Target,
|
|
10
|
+
Type: FileHtmlAttr.Type,
|
|
11
|
+
Lang: FileHtmlAttr.HrefLang,
|
|
12
|
+
};
|
|
13
|
+
export const YFM_FILE_DIRECTIVE_ATTRS = [
|
|
14
|
+
YfmFileAttr.ReferrerPolicy,
|
|
15
|
+
YfmFileAttr.Rel,
|
|
16
|
+
YfmFileAttr.Target,
|
|
17
|
+
YfmFileAttr.Type,
|
|
18
|
+
YfmFileAttr.Lang,
|
|
19
|
+
];
|
|
3
20
|
export const KNOWN_ATTRS = FILE_KNOWN_ATTRS.map((attrName) => {
|
|
4
21
|
if (attrName in FILE_TO_LINK_ATTRS_MAP)
|
|
5
22
|
return FILE_TO_LINK_ATTRS_MAP[attrName];
|
|
@@ -10,7 +27,9 @@ export const REQUIRED_ATTRS = FILE_REQUIRED_ATTRS.map((attrName) => {
|
|
|
10
27
|
return FILE_TO_LINK_ATTRS_MAP[attrName];
|
|
11
28
|
return attrName;
|
|
12
29
|
});
|
|
13
|
-
export const fileNodeAttrsSpec = {
|
|
30
|
+
export const fileNodeAttrsSpec = {
|
|
31
|
+
[YfmFileAttr.Markup]: { default: null },
|
|
32
|
+
};
|
|
14
33
|
for (const attrName of KNOWN_ATTRS) {
|
|
15
34
|
const attrSpec = (fileNodeAttrsSpec[attrName] = {});
|
|
16
35
|
if (!REQUIRED_ATTRS.includes(attrName)) {
|
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
import type { Extension } from '../../../../core';
|
|
2
|
-
export { yfmFileNodeName } from './const';
|
|
2
|
+
export { yfmFileNodeName, YfmFileAttr } from './const';
|
|
3
3
|
export declare const fileType: (schema: import("prosemirror-model").Schema<any, any>) => import("prosemirror-model").NodeType;
|
|
4
|
+
declare global {
|
|
5
|
+
namespace MarkdownEditor {
|
|
6
|
+
interface DirectiveSyntaxAdditionalSupportedExtensions {
|
|
7
|
+
yfmFile: true;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
}
|
|
4
11
|
export declare const YfmFileSpecs: Extension;
|
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
import { FILE_MARKUP_PREFIX, FileClassName, FileHtmlAttr, transform as fileTransform, } from '@diplodoc/file-extension';
|
|
2
2
|
import { nodeTypeFactory } from '../../../../utils/schema';
|
|
3
|
-
import { KNOWN_ATTRS, LINK_TO_FILE_ATTRS_MAP, fileNodeAttrsSpec, yfmFileNodeName } from './const';
|
|
4
|
-
export { yfmFileNodeName } from './const';
|
|
3
|
+
import { KNOWN_ATTRS, LINK_TO_FILE_ATTRS_MAP, YFM_FILE_DIRECTIVE_ATTRS, YfmFileAttr, fileNodeAttrsSpec, yfmFileNodeName, } from './const';
|
|
4
|
+
export { yfmFileNodeName, YfmFileAttr } from './const';
|
|
5
5
|
export const fileType = nodeTypeFactory(yfmFileNodeName);
|
|
6
6
|
export const YfmFileSpecs = (builder) => {
|
|
7
|
-
|
|
7
|
+
const directiveContext = builder.context.get('directiveSyntax');
|
|
8
|
+
builder.configureMd((md) => md.use(fileTransform({
|
|
9
|
+
bundle: false,
|
|
10
|
+
directiveSyntax: directiveContext === null || directiveContext === void 0 ? void 0 : directiveContext.mdPluginValueFor('yfmFile'),
|
|
11
|
+
})));
|
|
8
12
|
builder.addNode(yfmFileNodeName, () => ({
|
|
9
13
|
spec: {
|
|
10
14
|
group: 'inline',
|
|
@@ -48,15 +52,20 @@ export const YfmFileSpecs = (builder) => {
|
|
|
48
52
|
name: yfmFileNodeName,
|
|
49
53
|
type: 'node',
|
|
50
54
|
getAttrs: (tok) => {
|
|
51
|
-
|
|
52
|
-
|
|
55
|
+
const attrs = Object.fromEntries(tok.attrs || []);
|
|
56
|
+
attrs[YfmFileAttr.Markup] = tok.markup;
|
|
57
|
+
return attrs;
|
|
53
58
|
},
|
|
54
59
|
},
|
|
55
60
|
},
|
|
56
61
|
toMd: (state, node) => {
|
|
62
|
+
if (directiveContext === null || directiveContext === void 0 ? void 0 : directiveContext.shouldSerializeToDirective('yfmFile', node.attrs[YfmFileAttr.Markup])) {
|
|
63
|
+
state.write(serializeToDirective(node));
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
57
66
|
const attrsStr = Object.entries(node.attrs)
|
|
58
67
|
.reduce((arr, [key, value]) => {
|
|
59
|
-
if (value) {
|
|
68
|
+
if (key !== YfmFileAttr.Markup && value) {
|
|
60
69
|
if (key in LINK_TO_FILE_ATTRS_MAP) {
|
|
61
70
|
key = LINK_TO_FILE_ATTRS_MAP[key];
|
|
62
71
|
}
|
|
@@ -69,3 +78,17 @@ export const YfmFileSpecs = (builder) => {
|
|
|
69
78
|
},
|
|
70
79
|
}));
|
|
71
80
|
};
|
|
81
|
+
function serializeToDirective(node) {
|
|
82
|
+
const filename = node.attrs[YfmFileAttr.Name] || '';
|
|
83
|
+
const filelink = node.attrs[YfmFileAttr.Link] || '';
|
|
84
|
+
let fileMarkup = `:file[${filename}](${filelink})`;
|
|
85
|
+
const attrs = YFM_FILE_DIRECTIVE_ATTRS.reduce((acc, key) => {
|
|
86
|
+
const value = node.attrs[key];
|
|
87
|
+
if (value)
|
|
88
|
+
acc.push(`${key}="${value}"`);
|
|
89
|
+
return acc;
|
|
90
|
+
}, []);
|
|
91
|
+
if (attrs.length)
|
|
92
|
+
fileMarkup += `{${attrs.join(' ')}}`;
|
|
93
|
+
return fileMarkup;
|
|
94
|
+
}
|
|
@@ -20,6 +20,7 @@ export declare type CreateCodemirrorParams = {
|
|
|
20
20
|
onScroll: (event: Event) => void;
|
|
21
21
|
reactRenderer: ReactRenderStorage;
|
|
22
22
|
uploadHandler?: FileUploadHandler;
|
|
23
|
+
parseHtmlOnPaste?: boolean;
|
|
23
24
|
parseInsertedUrlAsImage?: ParseInsertedUrlAsImage;
|
|
24
25
|
needImageDimensions?: boolean;
|
|
25
26
|
enableNewImageSizeCalculation?: boolean;
|
|
@@ -3,19 +3,20 @@ import { defaultKeymap, history, historyKeymap, indentWithTab, insertNewlineKeep
|
|
|
3
3
|
import { syntaxHighlighting } from '@codemirror/language';
|
|
4
4
|
import { EditorView, keymap, placeholder } from '@codemirror/view';
|
|
5
5
|
import { ActionName } from '../../bundle/config/action-names';
|
|
6
|
-
import { DataTransferType } from '../../extensions/behavior/Clipboard/utils';
|
|
7
6
|
import { logger } from '../../logger';
|
|
8
7
|
import { Action as A, formatter as f } from '../../shortcuts';
|
|
8
|
+
import { DataTransferType, shouldSkipHtmlConversion } from '../../utils/clipboard';
|
|
9
9
|
import { insertImages, insertLink, toH1, toH2, toH3, toH4, toH5, toH6, toggleBold, toggleItalic, toggleStrikethrough, toggleUnderline, wrapToCodeBlock, wrapToInlineCode, wrapToYfmCut, wrapToYfmNote, } from '../commands';
|
|
10
10
|
import { DirectiveSyntaxFacet } from './directive-facet';
|
|
11
11
|
import { FileUploadHandlerFacet } from './files-upload-facet';
|
|
12
12
|
import { gravityHighlightStyle, gravityTheme } from './gravity';
|
|
13
|
+
import { MarkdownConverter } from './html-to-markdown/converters';
|
|
13
14
|
import { PairingCharactersExtension } from './pairing-chars';
|
|
14
15
|
import { ReactRendererFacet } from './react-facet';
|
|
15
16
|
import { SearchPanelPlugin } from './search-plugin/plugin';
|
|
16
17
|
import { yfmLang } from './yfm';
|
|
17
18
|
export function createCodemirror(params) {
|
|
18
|
-
const { doc, reactRenderer, onCancel, onScroll, onSubmit, onChange, onDocChange, disabledExtensions = {}, keymaps = [], receiver, yfmLangOptions, extensions: extraExtensions, placeholder: placeholderContent, autocompletion: autocompletionConfig, parseInsertedUrlAsImage, directiveSyntax, } = params;
|
|
19
|
+
const { doc, reactRenderer, onCancel, onScroll, onSubmit, onChange, onDocChange, disabledExtensions = {}, keymaps = [], receiver, yfmLangOptions, extensions: extraExtensions, placeholder: placeholderContent, autocompletion: autocompletionConfig, parseHtmlOnPaste, parseInsertedUrlAsImage, directiveSyntax, } = params;
|
|
19
20
|
const extensions = [gravityTheme, placeholder(placeholderContent)];
|
|
20
21
|
if (!disabledExtensions.history) {
|
|
21
22
|
extensions.push(history());
|
|
@@ -67,7 +68,43 @@ export function createCodemirror(params) {
|
|
|
67
68
|
},
|
|
68
69
|
paste(event, editor) {
|
|
69
70
|
var _a;
|
|
70
|
-
if (event.clipboardData
|
|
71
|
+
if (!event.clipboardData)
|
|
72
|
+
return;
|
|
73
|
+
// if clipboard contains YFM content - avoid any meddling with pasted content
|
|
74
|
+
// since text/yfm will contain valid markdown
|
|
75
|
+
const yfmContent = event.clipboardData.getData(DataTransferType.Yfm);
|
|
76
|
+
if (yfmContent) {
|
|
77
|
+
event.preventDefault();
|
|
78
|
+
editor.dispatch(editor.state.replaceSelection(yfmContent));
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
// checking if a copy buffer content is suitable for convertion
|
|
82
|
+
const shouldSkipHtml = shouldSkipHtmlConversion(event.clipboardData);
|
|
83
|
+
// if we have text/html inside copy/paste buffer
|
|
84
|
+
const htmlContent = event.clipboardData.getData(DataTransferType.Html);
|
|
85
|
+
// if we pasting markdown from VsCode we need skip html transformation
|
|
86
|
+
if (htmlContent && parseHtmlOnPaste && !shouldSkipHtml) {
|
|
87
|
+
let parsedMarkdownMarkup;
|
|
88
|
+
try {
|
|
89
|
+
const parser = new DOMParser();
|
|
90
|
+
const htmlDoc = parser.parseFromString(htmlContent, 'text/html');
|
|
91
|
+
const converter = new MarkdownConverter();
|
|
92
|
+
parsedMarkdownMarkup = converter.processNode(htmlDoc.body).trim();
|
|
93
|
+
}
|
|
94
|
+
catch (e) {
|
|
95
|
+
// The code is pretty new and there might be random issues we haven't caught yet,
|
|
96
|
+
// especially with invalid HTML or weird DOM parsing errors.
|
|
97
|
+
// If something goes wrong, I just want to fall back to the "default pasting"
|
|
98
|
+
// rather than break the entire experience for the user.
|
|
99
|
+
logger.error(e);
|
|
100
|
+
}
|
|
101
|
+
if (parsedMarkdownMarkup !== undefined) {
|
|
102
|
+
event.preventDefault();
|
|
103
|
+
editor.dispatch(editor.state.replaceSelection(parsedMarkdownMarkup));
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
if (parseInsertedUrlAsImage) {
|
|
71
108
|
const { imageUrl, title } = parseInsertedUrlAsImage((_a = event.clipboardData.getData(DataTransferType.Text)) !== null && _a !== void 0 ? _a : '') || {};
|
|
72
109
|
if (!imageUrl) {
|
|
73
110
|
return;
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Interface defining methods for visiting different types of HTML nodes.
|
|
3
|
+
* Each method corresponds to a specific HTML element type and returns its Markdown representation.
|
|
4
|
+
*/
|
|
5
|
+
export interface HTMLNodeVisitor {
|
|
6
|
+
/** Converts a text node to Markdown */
|
|
7
|
+
visitText(node: Text): string;
|
|
8
|
+
/** Converts an anchor element to Markdown link syntax */
|
|
9
|
+
visitLink(node: HTMLAnchorElement): string;
|
|
10
|
+
/** Converts a header element to Markdown heading syntax */
|
|
11
|
+
visitHeader(node: HTMLElement, level: number): string;
|
|
12
|
+
/** Converts a paragraph element to Markdown */
|
|
13
|
+
visitParagraph(node: HTMLElement): string;
|
|
14
|
+
/** Converts formatting elements (bold, italic, etc.) to Markdown */
|
|
15
|
+
visitFormatting(node: HTMLElement): string;
|
|
16
|
+
/** Converts code elements to Markdown inline code syntax */
|
|
17
|
+
visitCode(node: HTMLElement): string;
|
|
18
|
+
/** Handles generic HTML elements with no specific Markdown conversion */
|
|
19
|
+
visitGeneric(node: HTMLElement): string;
|
|
20
|
+
/** Converts an HTML div element to Markdown format, adding a single newline */
|
|
21
|
+
visitDiv(node: HTMLElement): string;
|
|
22
|
+
/** Converts an HTML br element to a newline in Markdown */
|
|
23
|
+
visitBr(): string;
|
|
24
|
+
/** Converts a table row element to Markdown */
|
|
25
|
+
visitTableRow(node: HTMLTableRowElement): string;
|
|
26
|
+
/** Converts an HTML table element to Markdown table format */
|
|
27
|
+
visitTable(node: HTMLTableElement): string;
|
|
28
|
+
/** Convert img tag to Markdown image format */
|
|
29
|
+
visitImage(node: HTMLImageElement): string;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Main converter class that implements the visitor interface to convert HTML to Markdown.
|
|
33
|
+
* Uses the Chain of Responsibility pattern for handling different node types.
|
|
34
|
+
*/
|
|
35
|
+
export declare class MarkdownConverter implements HTMLNodeVisitor {
|
|
36
|
+
private handler;
|
|
37
|
+
constructor();
|
|
38
|
+
/**
|
|
39
|
+
* Converts a text node to Markdown, escaping special characters.
|
|
40
|
+
*/
|
|
41
|
+
visitText(node: Text): string;
|
|
42
|
+
/**
|
|
43
|
+
* Converts an HTML anchor element to Markdown link syntax.
|
|
44
|
+
*/
|
|
45
|
+
visitLink(node: HTMLAnchorElement): string;
|
|
46
|
+
/**
|
|
47
|
+
* Converts an HTML heading element to Markdown heading syntax.
|
|
48
|
+
*/
|
|
49
|
+
visitHeader(node: HTMLElement, level: number): string;
|
|
50
|
+
/**
|
|
51
|
+
* Converts an HTML paragraph to Markdown format.
|
|
52
|
+
*/
|
|
53
|
+
visitParagraph(node: HTMLElement): string;
|
|
54
|
+
/**
|
|
55
|
+
* Applies Markdown formatting (bold, italic, etc.) to text content.
|
|
56
|
+
*/
|
|
57
|
+
visitFormatting(node: HTMLElement): string;
|
|
58
|
+
/**
|
|
59
|
+
* Converts HTML code elements to Markdown inline code syntax.
|
|
60
|
+
*/
|
|
61
|
+
visitCode(node: HTMLElement): string;
|
|
62
|
+
/**
|
|
63
|
+
* Handles generic HTML elements by processing their children.
|
|
64
|
+
*/
|
|
65
|
+
visitGeneric(node: HTMLElement): string;
|
|
66
|
+
/**
|
|
67
|
+
* Converts an HTML div element to Markdown format, adding a single newline.
|
|
68
|
+
*/
|
|
69
|
+
visitDiv(node: HTMLElement): string;
|
|
70
|
+
/**
|
|
71
|
+
* Converts an HTML br element to a newline in Markdown.
|
|
72
|
+
*/
|
|
73
|
+
visitBr(): string;
|
|
74
|
+
/**
|
|
75
|
+
* Converts an HTML table row element to Markdown table row format.
|
|
76
|
+
*/
|
|
77
|
+
visitTableRow(node: HTMLTableRowElement): string;
|
|
78
|
+
/**
|
|
79
|
+
* Converts an HTML table element to Markdown table format.
|
|
80
|
+
*/
|
|
81
|
+
visitTable(node: HTMLTableElement): string;
|
|
82
|
+
/**
|
|
83
|
+
* Converts img tag to Markdown image format
|
|
84
|
+
*/
|
|
85
|
+
visitImage(node: HTMLImageElement): string;
|
|
86
|
+
/**
|
|
87
|
+
* Processes a single node using the handler chain.
|
|
88
|
+
*/
|
|
89
|
+
processNode(node: Node): string;
|
|
90
|
+
/**
|
|
91
|
+
* Creates and links together handlers in a specific order implementing the Chain of Responsibility pattern.
|
|
92
|
+
* @returns The first handler in the chain
|
|
93
|
+
*/
|
|
94
|
+
private setupHandlerChain;
|
|
95
|
+
/**
|
|
96
|
+
* Recursively collects and processes text content from a node and its children.
|
|
97
|
+
*/
|
|
98
|
+
private collectTextContent;
|
|
99
|
+
/**
|
|
100
|
+
* Collects raw text content from code elements.
|
|
101
|
+
*/
|
|
102
|
+
private collectCodeContent;
|
|
103
|
+
/**
|
|
104
|
+
* Processes all child nodes of a given node.
|
|
105
|
+
*/
|
|
106
|
+
private processChildren;
|
|
107
|
+
/**
|
|
108
|
+
* Gets the first handler in the chain.
|
|
109
|
+
*/
|
|
110
|
+
private getHandler;
|
|
111
|
+
}
|