@gravity-ui/markdown-editor 13.3.0 → 13.4.0

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.
Files changed (106) hide show
  1. package/build/cjs/bundle/HorizontalDrag.d.ts +1 -0
  2. package/build/cjs/bundle/HorizontalDrag.js +2 -1
  3. package/build/cjs/bundle/config/action-names.d.ts +1 -1
  4. package/build/cjs/bundle/config/action-names.js +1 -0
  5. package/build/cjs/bundle/config/icons.d.ts +1 -1
  6. package/build/cjs/bundle/config/icons.js +1 -0
  7. package/build/cjs/bundle/config/markup.d.ts +1 -0
  8. package/build/cjs/bundle/config/markup.js +10 -1
  9. package/build/cjs/bundle/config/wysiwyg.d.ts +1 -0
  10. package/build/cjs/bundle/config/wysiwyg.js +12 -1
  11. package/build/cjs/extensions/yfm/YfmHtmlBlock/YfmHtmlBlockNodeView/NodeView.d.ts +23 -0
  12. package/build/cjs/extensions/yfm/YfmHtmlBlock/YfmHtmlBlockNodeView/NodeView.js +57 -0
  13. package/build/cjs/extensions/yfm/YfmHtmlBlock/YfmHtmlBlockNodeView/YfmHtmlBlock.css +66 -0
  14. package/build/cjs/extensions/yfm/YfmHtmlBlock/YfmHtmlBlockNodeView/YfmHtmlBlockView.d.ts +17 -0
  15. package/build/cjs/extensions/yfm/YfmHtmlBlock/YfmHtmlBlockNodeView/YfmHtmlBlockView.js +178 -0
  16. package/build/cjs/extensions/yfm/YfmHtmlBlock/YfmHtmlBlockNodeView/index.d.ts +1 -0
  17. package/build/cjs/extensions/yfm/YfmHtmlBlock/YfmHtmlBlockNodeView/index.js +4 -0
  18. package/build/cjs/extensions/yfm/YfmHtmlBlock/YfmHtmlBlockSpecs/const.d.ts +15 -0
  19. package/build/cjs/extensions/yfm/YfmHtmlBlock/YfmHtmlBlockSpecs/const.js +20 -0
  20. package/build/cjs/extensions/yfm/YfmHtmlBlock/YfmHtmlBlockSpecs/index.d.ts +10 -0
  21. package/build/cjs/extensions/yfm/YfmHtmlBlock/YfmHtmlBlockSpecs/index.js +43 -0
  22. package/build/cjs/extensions/yfm/YfmHtmlBlock/actions.d.ts +2 -0
  23. package/build/cjs/extensions/yfm/YfmHtmlBlock/actions.js +15 -0
  24. package/build/cjs/extensions/yfm/YfmHtmlBlock/const.d.ts +2 -0
  25. package/build/cjs/extensions/yfm/YfmHtmlBlock/const.js +6 -0
  26. package/build/cjs/extensions/yfm/YfmHtmlBlock/index.d.ts +14 -0
  27. package/build/cjs/extensions/yfm/YfmHtmlBlock/index.js +17 -0
  28. package/build/cjs/extensions/yfm/index.d.ts +1 -1
  29. package/build/cjs/extensions/yfm/index.js +1 -1
  30. package/build/cjs/i18n/common/en.json +6 -2
  31. package/build/cjs/i18n/common/index.d.ts +7 -3
  32. package/build/cjs/i18n/common/ru.json +6 -2
  33. package/build/cjs/i18n/menubar/en.json +37 -36
  34. package/build/cjs/i18n/menubar/index.d.ts +38 -37
  35. package/build/cjs/i18n/menubar/ru.json +37 -36
  36. package/build/cjs/icons/index.d.ts +1 -1
  37. package/build/cjs/icons/index.js +2 -1
  38. package/build/cjs/markup/commands/blocks.d.ts +1 -0
  39. package/build/cjs/markup/commands/blocks.js +12 -1
  40. package/build/cjs/version.js +1 -1
  41. package/build/cjs/view/components/YfmHtml/{YfmHtml.d.ts → YfmStaticView.d.ts} +2 -2
  42. package/build/cjs/view/components/YfmHtml/{YfmHtml.js → YfmStaticView.js} +2 -2
  43. package/build/cjs/view/components/YfmHtml/index.d.ts +12 -1
  44. package/build/cjs/view/components/YfmHtml/index.js +4 -2
  45. package/build/cjs/view/hocs/withYfmHtml/index.d.ts +13 -0
  46. package/build/cjs/view/hocs/withYfmHtml/index.js +24 -0
  47. package/build/cjs/view/hocs/withYfmHtml/types.d.ts +3 -0
  48. package/build/cjs/view/hocs/withYfmHtml/types.js +2 -0
  49. package/build/cjs/view/hocs/withYfmHtml/useYfmHtmlBlockRuntime.d.ts +1 -0
  50. package/build/cjs/view/hocs/withYfmHtml/useYfmHtmlBlockRuntime.js +34 -0
  51. package/build/cjs/view/hocs/withYfmHtml/utils.d.ts +16 -0
  52. package/build/cjs/view/hocs/withYfmHtml/utils.js +26 -0
  53. package/build/esm/bundle/HorizontalDrag.d.ts +1 -0
  54. package/build/esm/bundle/HorizontalDrag.js +2 -1
  55. package/build/esm/bundle/config/action-names.d.ts +1 -1
  56. package/build/esm/bundle/config/action-names.js +1 -0
  57. package/build/esm/bundle/config/icons.d.ts +1 -1
  58. package/build/esm/bundle/config/icons.js +2 -1
  59. package/build/esm/bundle/config/markup.d.ts +1 -0
  60. package/build/esm/bundle/config/markup.js +10 -1
  61. package/build/esm/bundle/config/wysiwyg.d.ts +1 -0
  62. package/build/esm/bundle/config/wysiwyg.js +11 -0
  63. package/build/esm/extensions/yfm/YfmHtmlBlock/YfmHtmlBlockNodeView/NodeView.d.ts +23 -0
  64. package/build/esm/extensions/yfm/YfmHtmlBlock/YfmHtmlBlockNodeView/NodeView.js +52 -0
  65. package/build/esm/extensions/yfm/YfmHtmlBlock/YfmHtmlBlockNodeView/YfmHtmlBlock.css +66 -0
  66. package/build/esm/extensions/yfm/YfmHtmlBlock/YfmHtmlBlockNodeView/YfmHtmlBlockView.d.ts +18 -0
  67. package/build/esm/extensions/yfm/YfmHtmlBlock/YfmHtmlBlockNodeView/YfmHtmlBlockView.js +173 -0
  68. package/build/esm/extensions/yfm/YfmHtmlBlock/YfmHtmlBlockNodeView/index.d.ts +1 -0
  69. package/build/esm/extensions/yfm/YfmHtmlBlock/YfmHtmlBlockNodeView/index.js +1 -0
  70. package/build/esm/extensions/yfm/YfmHtmlBlock/YfmHtmlBlockSpecs/const.d.ts +15 -0
  71. package/build/esm/extensions/yfm/YfmHtmlBlock/YfmHtmlBlockSpecs/const.js +17 -0
  72. package/build/esm/extensions/yfm/YfmHtmlBlock/YfmHtmlBlockSpecs/index.d.ts +10 -0
  73. package/build/esm/extensions/yfm/YfmHtmlBlock/YfmHtmlBlockSpecs/index.js +39 -0
  74. package/build/esm/extensions/yfm/YfmHtmlBlock/actions.d.ts +2 -0
  75. package/build/esm/extensions/yfm/YfmHtmlBlock/actions.js +12 -0
  76. package/build/esm/extensions/yfm/YfmHtmlBlock/const.d.ts +2 -0
  77. package/build/esm/extensions/yfm/YfmHtmlBlock/const.js +2 -0
  78. package/build/esm/extensions/yfm/YfmHtmlBlock/index.d.ts +14 -0
  79. package/build/esm/extensions/yfm/YfmHtmlBlock/index.js +13 -0
  80. package/build/esm/extensions/yfm/index.d.ts +1 -1
  81. package/build/esm/extensions/yfm/index.js +1 -1
  82. package/build/esm/i18n/common/en.json +6 -2
  83. package/build/esm/i18n/common/index.d.ts +7 -3
  84. package/build/esm/i18n/common/ru.json +6 -2
  85. package/build/esm/i18n/menubar/en.json +37 -36
  86. package/build/esm/i18n/menubar/index.d.ts +38 -37
  87. package/build/esm/i18n/menubar/ru.json +37 -36
  88. package/build/esm/icons/index.d.ts +1 -1
  89. package/build/esm/icons/index.js +1 -1
  90. package/build/esm/markup/commands/blocks.d.ts +1 -0
  91. package/build/esm/markup/commands/blocks.js +10 -0
  92. package/build/esm/version.js +1 -1
  93. package/build/esm/view/components/YfmHtml/{YfmHtml.d.ts → YfmStaticView.d.ts} +2 -2
  94. package/build/esm/view/components/YfmHtml/{YfmHtml.js → YfmStaticView.js} +1 -1
  95. package/build/esm/view/components/YfmHtml/index.d.ts +12 -1
  96. package/build/esm/view/components/YfmHtml/index.js +6 -1
  97. package/build/esm/view/hocs/withYfmHtml/index.d.ts +13 -0
  98. package/build/esm/view/hocs/withYfmHtml/index.js +19 -0
  99. package/build/esm/view/hocs/withYfmHtml/types.d.ts +3 -0
  100. package/build/esm/view/hocs/withYfmHtml/types.js +1 -0
  101. package/build/esm/view/hocs/withYfmHtml/useYfmHtmlBlockRuntime.d.ts +1 -0
  102. package/build/esm/view/hocs/withYfmHtml/useYfmHtmlBlockRuntime.js +7 -0
  103. package/build/esm/view/hocs/withYfmHtml/utils.d.ts +16 -0
  104. package/build/esm/view/hocs/withYfmHtml/utils.js +22 -0
  105. package/build/styles.css +66 -0
  106. package/package.json +6 -1
@@ -1,3 +1,3 @@
1
- declare const namesObj: Record<"bold" | "link" | "italic" | "strike" | "underline" | "mark" | "quote" | "mono" | "paragraph" | "anchor" | "table" | "image" | "code_inline" | "code_block" | "file" | "checkbox" | "bulletList" | "orderedList" | "emoji" | "tabs" | "yfm_cut" | "heading1" | "heading2" | "heading3" | "heading4" | "heading5" | "heading6" | "yfm_note" | "undo" | "redo" | "math_inline" | "math_block" | "mermaid" | "liftListItem" | "sinkListItem" | "yfm_block" | "yfm_layout" | "horizontalrule", string>;
1
+ declare const namesObj: Record<"bold" | "link" | "italic" | "strike" | "underline" | "mark" | "quote" | "mono" | "paragraph" | "anchor" | "table" | "image" | "code_inline" | "code_block" | "file" | "checkbox" | "bulletList" | "orderedList" | "emoji" | "tabs" | "yfm_cut" | "heading1" | "heading2" | "heading3" | "heading4" | "heading5" | "heading6" | "yfm_note" | "undo" | "redo" | "math_inline" | "math_block" | "mermaid" | "liftListItem" | "sinkListItem" | "yfm_block" | "yfm_html_block" | "yfm_layout" | "horizontalrule", string>;
2
2
  export declare const ActionName: Readonly<typeof namesObj>;
3
3
  export {};
@@ -24,6 +24,7 @@ const names = [
24
24
  'yfm_cut',
25
25
  'yfm_note',
26
26
  'yfm_block',
27
+ 'yfm_html_block',
27
28
  'yfm_layout',
28
29
  'table',
29
30
  'code_inline',
@@ -1,5 +1,5 @@
1
1
  import { ToolbarIconData } from '../../toolbar/types';
2
- declare type Icon = 'undo' | 'redo' | 'bold' | 'italic' | 'underline' | 'strikethrough' | 'mono' | 'mark' | 'textColor' | 'text' | 'headline' | 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'bulletList' | 'orderedList' | 'sink' | 'lift' | 'cut' | 'note' | 'code' | 'codeBlock' | 'link' | 'image' | 'table' | 'quote' | 'checklist' | 'horizontalRule' | 'file' | 'functionInline' | 'functionBlock' | 'emoji' | 'tabs' | 'mermaid';
2
+ declare type Icon = 'undo' | 'redo' | 'bold' | 'italic' | 'underline' | 'strikethrough' | 'mono' | 'mark' | 'textColor' | 'text' | 'headline' | 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'bulletList' | 'orderedList' | 'sink' | 'lift' | 'cut' | 'note' | 'code' | 'codeBlock' | 'link' | 'image' | 'table' | 'quote' | 'checklist' | 'horizontalRule' | 'file' | 'functionInline' | 'functionBlock' | 'emoji' | 'tabs' | 'mermaid' | 'html';
3
3
  declare type Icons = Record<Icon, ToolbarIconData>;
4
4
  export declare const icons: Icons;
5
5
  export {};
@@ -1,4 +1,4 @@
1
- import { BoldIcon, CheckListIcon, CodeBlockIcon, CodeInlineIcon, CutIcon, EmojiIcon, FileIcon, FunctionBlockIcon, FunctionInlineIcon, HRuleIcon, Heading1Icon, Heading2Icon, Heading3Icon, Heading4Icon, Heading5Icon, Heading6Icon, HeadingIcon, ImageIcon, ItalicIcon, LiftIcon, LinkIcon, ListBlIcon, ListOlIcon, MarkIcon, MermaidIcon, MonoIcon, NoteIcon, QuoteIcon, RedoIcon, SinkIcon, StrikethroughIcon, TableIcon, TabsIcon, TextColorIcon, TextIcon, UnderlineIcon, UndoIcon, } from '../../icons';
1
+ import { BoldIcon, CheckListIcon, CodeBlockIcon, CodeInlineIcon, CutIcon, EmojiIcon, FileIcon, FunctionBlockIcon, FunctionInlineIcon, HRuleIcon, Heading1Icon, Heading2Icon, Heading3Icon, Heading4Icon, Heading5Icon, Heading6Icon, HeadingIcon, HtmlBlockIcon, ImageIcon, ItalicIcon, LiftIcon, LinkIcon, ListBlIcon, ListOlIcon, MarkIcon, MermaidIcon, MonoIcon, NoteIcon, QuoteIcon, RedoIcon, SinkIcon, StrikethroughIcon, TableIcon, TabsIcon, TextColorIcon, TextIcon, UnderlineIcon, UndoIcon, } from '../../icons';
2
2
  export const icons = {
3
3
  undo: { data: UndoIcon },
4
4
  redo: { data: RedoIcon },
@@ -30,6 +30,7 @@ export const icons = {
30
30
  table: { data: TableIcon },
31
31
  quote: { data: QuoteIcon },
32
32
  checklist: { data: CheckListIcon },
33
+ html: { data: HtmlBlockIcon },
33
34
  horizontalRule: { data: HRuleIcon },
34
35
  file: { data: FileIcon },
35
36
  functionInline: { data: FunctionInlineIcon },
@@ -35,6 +35,7 @@ export declare const mCodeListConfig: MToolbarListButtonData;
35
35
  export declare const mMathListConfig: MToolbarListButtonData;
36
36
  export declare const mMathListItem: MToolbarListItemData;
37
37
  export declare const mMermaidButton: MToolbarSingleItemData;
38
+ export declare const mYfmHtmlBlockButton: MToolbarSingleItemData;
38
39
  export declare const mImagePopupData: MToolbarButtonPopupData;
39
40
  export declare const mFilePopupData: MToolbarButtonPopupData;
40
41
  /** prepared markup toolbar config */
@@ -1,6 +1,6 @@
1
1
  import React from 'react';
2
2
  import { i18n } from '../../i18n/menubar';
3
- import { insertHRule, insertLink, insertMermaidDiagram, insertYfmTable, insertYfmTabs, liftListItem, redo, redoDepth, sinkListItem, toBulletList, toH1, toH2, toH3, toH4, toH5, toH6, toOrderedList, toggleBold, toggleItalic, toggleMarked, toggleMonospace, toggleStrikethrough, toggleUnderline, undo, undoDepth, wrapToBlockquote, wrapToCheckbox, wrapToCodeBlock, wrapToInlineCode, wrapToMathBlock, wrapToMathInline, wrapToYfmCut, wrapToYfmNote, } from '../../markup/commands';
3
+ import { insertHRule, insertLink, insertMermaidDiagram, insertYfmHtmlBlock, insertYfmTable, insertYfmTabs, liftListItem, redo, redoDepth, sinkListItem, toBulletList, toH1, toH2, toH3, toH4, toH5, toH6, toOrderedList, toggleBold, toggleItalic, toggleMarked, toggleMonospace, toggleStrikethrough, toggleUnderline, undo, undoDepth, wrapToBlockquote, wrapToCheckbox, wrapToCodeBlock, wrapToInlineCode, wrapToMathBlock, wrapToMathInline, wrapToYfmCut, wrapToYfmNote, } from '../../markup/commands';
4
4
  import { Action as A, formatter as f } from '../../shortcuts';
5
5
  import { ToolbarDataType, } from '../../toolbar/types';
6
6
  import { MToolbarColors } from '../toolbar/markup/MToolbarColors';
@@ -356,6 +356,15 @@ export const mMermaidButton = {
356
356
  isActive: isActiveFn,
357
357
  isEnable: isEnableFn,
358
358
  };
359
+ export const mYfmHtmlBlockButton = {
360
+ id: ActionName.yfm_html_block,
361
+ type: ToolbarDataType.SingleButton,
362
+ title: i18n.bind(null, 'html'),
363
+ icon: icons.html,
364
+ exec: (e) => insertYfmHtmlBlock(e.cm),
365
+ isActive: isActiveFn,
366
+ isEnable: isEnableFn,
367
+ };
359
368
  export const mImagePopupData = {
360
369
  id: 'image',
361
370
  type: ToolbarDataType.ButtonPopup,
@@ -44,6 +44,7 @@ export declare const wTabsItemData: WToolbarSingleItemData;
44
44
  export declare const wMathBlockItemData: WToolbarSingleItemData;
45
45
  export declare const wMathListConfig: WToolbarListButtonData;
46
46
  export declare const wMathListItem: WToolbarListItemData;
47
+ export declare const wYfmHtmlBlockItemData: WToolbarSingleItemData;
47
48
  export declare const wCommandMenuConfig: WToolbarItemData[];
48
49
  export declare const wHiddenData: WToolbarItemData[];
49
50
  /** prepared wysiwyg toolbar config */
@@ -391,6 +391,15 @@ export const wMathListConfig = {
391
391
  data: [wMathInlineItemData, wMathBlockItemData],
392
392
  };
393
393
  export const wMathListItem = Object.assign({ id: 'math', type: ToolbarDataType.ListButton }, wMathListConfig);
394
+ export const wYfmHtmlBlockItemData = {
395
+ id: ActionName.yfm_html_block,
396
+ type: ToolbarDataType.SingleButton,
397
+ title: i18n.bind(null, 'html'),
398
+ icon: icons.html,
399
+ exec: (e) => e.actions.createYfmHtmlBlock.run(),
400
+ isActive: (e) => e.actions.createYfmHtmlBlock.isActive(),
401
+ isEnable: (e) => e.actions.createYfmHtmlBlock.isEnable(),
402
+ };
394
403
  export const wCommandMenuConfig = [
395
404
  ...wHeadingListConfig.data,
396
405
  ...wListsListConfig.data,
@@ -408,6 +417,7 @@ export const wCommandMenuConfig = [
408
417
  // wMathInlineItemData,
409
418
  // wMathBlockItemData,
410
419
  wTabsItemData,
420
+ wYfmHtmlBlockItemData,
411
421
  ];
412
422
  export const wHiddenData = wCommandMenuConfig;
413
423
  /** prepared wysiwyg toolbar config */
@@ -543,6 +553,7 @@ export const wCommandMenuConfigByPreset = {
543
553
  wHruleItemData,
544
554
  wFileItemData,
545
555
  wTabsItemData,
556
+ wYfmHtmlBlockItemData,
546
557
  ],
547
558
  full: wCommandMenuConfig.slice(),
548
559
  };
@@ -0,0 +1,23 @@
1
+ import { Node } from 'prosemirror-model';
2
+ import { EditorView, NodeView } from 'prosemirror-view';
3
+ import { YfmHtmlBlockOptions } from '../index';
4
+ export declare class WYfmHtmlBlockNodeView implements NodeView {
5
+ readonly dom: HTMLElement;
6
+ private node;
7
+ private readonly view;
8
+ private readonly getPos;
9
+ private readonly options;
10
+ private readonly renderItem;
11
+ constructor({ node, view, getPos, options, }: {
12
+ node: Node;
13
+ view: EditorView;
14
+ getPos: () => number | undefined;
15
+ options: YfmHtmlBlockOptions;
16
+ });
17
+ update(node: Node): boolean;
18
+ destroy(): void;
19
+ ignoreMutation(): boolean;
20
+ stopEvent(e: Event): boolean;
21
+ private onChange;
22
+ private renderYfmHtmlBlock;
23
+ }
@@ -0,0 +1,52 @@
1
+ import React from 'react';
2
+ import { createPortal } from 'react-dom';
3
+ import { getReactRendererFromState } from '../../../behavior';
4
+ import { YfmHtmlBlockConsts } from '../YfmHtmlBlockSpecs/const';
5
+ import { YfmHtmlBlockView } from './YfmHtmlBlockView';
6
+ export class WYfmHtmlBlockNodeView {
7
+ constructor({ node, view, getPos, options, }) {
8
+ this.options = {};
9
+ this.node = node;
10
+ this.dom = document.createElement('div');
11
+ this.dom.classList.add('yfm-html-block-container');
12
+ this.dom.contentEditable = 'false';
13
+ this.view = view;
14
+ this.getPos = getPos;
15
+ this.options = options;
16
+ this.renderItem = getReactRendererFromState(view.state).createItem('yfmHtmlBlock-view', this.renderYfmHtmlBlock.bind(this));
17
+ }
18
+ update(node) {
19
+ if (node.type !== this.node.type)
20
+ return false;
21
+ if (node.attrs[YfmHtmlBlockConsts.NodeAttrs.newCreated] !==
22
+ this.node.attrs[YfmHtmlBlockConsts.NodeAttrs.newCreated])
23
+ return false;
24
+ this.node = node;
25
+ this.renderItem.rerender();
26
+ return true;
27
+ }
28
+ destroy() {
29
+ this.renderItem.remove();
30
+ }
31
+ ignoreMutation() {
32
+ return true;
33
+ }
34
+ stopEvent(e) {
35
+ const target = e.target;
36
+ if (typeof target.className === 'string' &&
37
+ target.className.includes('prosemirror-stop-event')) {
38
+ return true;
39
+ }
40
+ return false;
41
+ }
42
+ onChange(attrs) {
43
+ const pos = this.getPos();
44
+ if (pos === undefined)
45
+ return;
46
+ const tr = this.view.state.tr.setNodeMarkup(pos, undefined, Object.assign(Object.assign({}, this.node.attrs), attrs), []);
47
+ this.view.dispatch(tr);
48
+ }
49
+ renderYfmHtmlBlock() {
50
+ return createPortal(React.createElement(YfmHtmlBlockView, { useConfig: this.options.useConfig, view: this.view, onChange: this.onChange.bind(this), node: this.node, getPos: this.getPos }), this.dom);
51
+ }
52
+ }
@@ -0,0 +1,66 @@
1
+ .g-md-yfm-html-block {
2
+ position: relative;
3
+ display: flex;
4
+ justify-content: space-between;
5
+ margin-bottom: 15px;
6
+ padding-top: 28px;
7
+ border: 1px solid var(--g-color-line-generic);
8
+ border-radius: var(--g-border-radius-m);
9
+ }
10
+ .g-md-yfm-html-block_editing {
11
+ display: flex;
12
+ padding-top: 0;
13
+ border: 0;
14
+ }
15
+ .g-md-yfm-html-block__label {
16
+ position: absolute;
17
+ top: 8px;
18
+ left: 8px;
19
+ }
20
+ .g-md-yfm-html-block__menu {
21
+ position: absolute;
22
+ top: 0;
23
+ right: 0;
24
+ }
25
+ .g-md-yfm-html-block__preview {
26
+ flex: 1;
27
+ }
28
+ .g-md-yfm-html-block__error {
29
+ flex: 1;
30
+ font-family: var(--g-font-family-monospace);
31
+ color: var(--g-color-text-danger);
32
+ }
33
+ .g-md-yfm-html-block__editor {
34
+ flex: 1;
35
+ width: 50%;
36
+ white-space: nowrap;
37
+ caret-color: auto;
38
+ }
39
+ .g-md-yfm-html-block__editor .g-text-area__content {
40
+ font-size: 1em;
41
+ color: var(--yfm-color-hljs-subst);
42
+ border: 0;
43
+ border-radius: var(--g-border-radius-m);
44
+ background: var(--yfm-color-hljs-background);
45
+ font-feature-settings: normal;
46
+ }
47
+ .g-md-yfm-html-block__editor .g-text-area__control.g-md-YfmHtmlBlockHelper {
48
+ font-family: var(--yfm-font-family-monospace);
49
+ }
50
+ .g-md-yfm-html-block__editor-popover {
51
+ z-index: 1;
52
+ float: right;
53
+ }
54
+ .g-md-yfm-html-block__controls {
55
+ display: flex;
56
+ justify-content: end;
57
+ margin-top: 5px;
58
+ }
59
+ .g-md-yfm-html-block__content {
60
+ flex-grow: 1;
61
+ }
62
+
63
+ .g-root_theme_dark-hc .g-md-yfm-html-block_editing .g-text-area__content,
64
+ .g-root_theme_dark .g-md-yfm-html-block_editing .g-text-area__content {
65
+ color: var(--g-color-text-primary);
66
+ }
@@ -0,0 +1,18 @@
1
+ import React from 'react';
2
+ import type { IHTMLIFrameElementConfig } from '@diplodoc/html-extension/runtime';
3
+ import { Node } from 'prosemirror-model';
4
+ import { EditorView } from 'prosemirror-view';
5
+ import { YfmHtmlBlockConsts } from '../YfmHtmlBlockSpecs/const';
6
+ export declare const cnYfmHtmlBlock: import("@bem-react/classname").ClassNameFormatter;
7
+ export declare const cnHelper: import("@bem-react/classname").ClassNameFormatter;
8
+ import './YfmHtmlBlock.css';
9
+ export declare function generateID(): string;
10
+ export declare const YfmHtmlBlockView: React.FC<{
11
+ view: EditorView;
12
+ onChange: (attrs: {
13
+ [YfmHtmlBlockConsts.NodeAttrs.srcdoc]: string;
14
+ }) => void;
15
+ node: Node;
16
+ getPos: () => number | undefined;
17
+ useConfig?: () => IHTMLIFrameElementConfig | undefined;
18
+ }>;
@@ -0,0 +1,173 @@
1
+ import React, { useEffect, useRef, useState } from 'react';
2
+ import { Ellipsis as DotsIcon, Eye } from '@gravity-ui/icons';
3
+ import { Button, Icon, Label, Menu, Popup } from '@gravity-ui/uikit';
4
+ import debounce from 'lodash/debounce';
5
+ import { cn } from '../../../../classname';
6
+ import { TextAreaFixed as TextArea } from '../../../../forms/TextInput';
7
+ import { i18n } from '../../../../i18n/common';
8
+ import { useBooleanState } from '../../../../react-utils/hooks';
9
+ import { removeNode } from '../../../../utils/remove-node';
10
+ import { YfmHtmlBlockConsts } from '../YfmHtmlBlockSpecs/const';
11
+ export const cnYfmHtmlBlock = cn('yfm-html-block');
12
+ export const cnHelper = cn('yfm-html-block-helper');
13
+ import './YfmHtmlBlock.css';
14
+ const b = cnYfmHtmlBlock;
15
+ export function generateID() {
16
+ return Math.random().toString(36).substr(2, 8);
17
+ }
18
+ const DEFAULT_PADDING = 20;
19
+ const DEFAULT_DELAY = 100;
20
+ const YfmHtmlBlockPreview = ({ html, onСlick, config }) => {
21
+ var _a, _b, _c, _d, _e, _f;
22
+ const ref = useRef(null);
23
+ const styles = useRef({});
24
+ const classNames = useRef([]);
25
+ const resizeConfig = useRef({});
26
+ const [height, setHeight] = useState('100%');
27
+ useEffect(() => {
28
+ var _a, _b;
29
+ resizeConfig.current = {
30
+ padding: (_a = config === null || config === void 0 ? void 0 : config.resizePadding) !== null && _a !== void 0 ? _a : DEFAULT_PADDING,
31
+ delay: (_b = config === null || config === void 0 ? void 0 : config.resizeDelay) !== null && _b !== void 0 ? _b : DEFAULT_DELAY,
32
+ };
33
+ setStyles(config === null || config === void 0 ? void 0 : config.styles);
34
+ setClassNames(config === null || config === void 0 ? void 0 : config.classNames);
35
+ }, [config, (_c = (_b = (_a = ref.current) === null || _a === void 0 ? void 0 : _a.contentWindow) === null || _b === void 0 ? void 0 : _b.document) === null || _c === void 0 ? void 0 : _c.body]);
36
+ const handleLoadIFrame = () => {
37
+ var _a;
38
+ const contentWindow = (_a = ref.current) === null || _a === void 0 ? void 0 : _a.contentWindow;
39
+ handleResizeIFrame();
40
+ if (contentWindow) {
41
+ const frameDocument = contentWindow.document;
42
+ frameDocument.addEventListener('dblclick', () => {
43
+ onСlick();
44
+ });
45
+ }
46
+ };
47
+ const handleResizeIFrame = () => {
48
+ var _a, _b;
49
+ if (ref.current) {
50
+ const contentWindow = (_a = ref.current) === null || _a === void 0 ? void 0 : _a.contentWindow;
51
+ if (contentWindow) {
52
+ const body = contentWindow.document.body;
53
+ if (body) {
54
+ const height = body.scrollHeight +
55
+ (((_b = resizeConfig.current) === null || _b === void 0 ? void 0 : _b.padding) || DEFAULT_PADDING) +
56
+ 'px';
57
+ setHeight(height);
58
+ }
59
+ }
60
+ }
61
+ };
62
+ const setClassNames = (newClassNames) => {
63
+ var _a, _b;
64
+ const body = (_b = (_a = ref.current) === null || _a === void 0 ? void 0 : _a.contentWindow) === null || _b === void 0 ? void 0 : _b.document.body;
65
+ if (body && newClassNames) {
66
+ const previousClassNames = classNames.current;
67
+ // remove all classes that were in previousClassNames but are not in classNames
68
+ previousClassNames.forEach((className) => {
69
+ if (!newClassNames.includes(className)) {
70
+ body.classList.remove(className);
71
+ }
72
+ });
73
+ // add classes that are in classNames
74
+ newClassNames.forEach((className) => {
75
+ if (!body.classList.contains(className)) {
76
+ body.classList.add(className);
77
+ }
78
+ });
79
+ classNames.current = newClassNames;
80
+ }
81
+ };
82
+ const setStyles = (newStyles) => {
83
+ var _a, _b;
84
+ const body = (_b = (_a = ref.current) === null || _a === void 0 ? void 0 : _a.contentWindow) === null || _b === void 0 ? void 0 : _b.document.body;
85
+ if (body && newStyles) {
86
+ const previousStyles = styles.current;
87
+ // remove all styles that are in previousStyles but not in styles
88
+ Object.keys(previousStyles).forEach((property) => {
89
+ if (!Object.prototype.hasOwnProperty.call(newStyles, property)) {
90
+ body.style.removeProperty(property);
91
+ }
92
+ });
93
+ // sdd or update styles that are in styles
94
+ Object.keys(newStyles).forEach((property) => {
95
+ body.style.setProperty(property, newStyles[property]);
96
+ });
97
+ // update current styles to the new styles
98
+ styles.current = newStyles;
99
+ }
100
+ };
101
+ useEffect(() => {
102
+ var _a;
103
+ (_a = ref.current) === null || _a === void 0 ? void 0 : _a.addEventListener('load', handleLoadIFrame);
104
+ return () => {
105
+ var _a;
106
+ (_a = ref.current) === null || _a === void 0 ? void 0 : _a.removeEventListener('load', handleLoadIFrame);
107
+ };
108
+ }, [html]);
109
+ useEffect(() => {
110
+ var _a, _b;
111
+ if (ref.current) {
112
+ const resizeObserver = new window.ResizeObserver(debounce(handleResizeIFrame, (_b = (_a = resizeConfig.current) === null || _a === void 0 ? void 0 : _a.delay) !== null && _b !== void 0 ? _b : DEFAULT_DELAY));
113
+ resizeObserver.observe(ref.current);
114
+ }
115
+ }, [(_f = (_e = (_d = ref.current) === null || _d === void 0 ? void 0 : _d.contentWindow) === null || _e === void 0 ? void 0 : _e.document) === null || _f === void 0 ? void 0 : _f.body]);
116
+ return (React.createElement("iframe", { style: {
117
+ height,
118
+ }, ref: ref, title: generateID(), frameBorder: 0, className: b('content'), srcDoc: html }));
119
+ };
120
+ const CodeEditMode = ({ initialText, onSave, onCancel }) => {
121
+ const [text, setText] = useState(initialText || '\n');
122
+ return (React.createElement("div", { className: b({ editing: true }) },
123
+ React.createElement("div", { className: b('editor') },
124
+ React.createElement(TextArea, { controlProps: {
125
+ className: cnHelper({ 'prosemirror-stop-event': true }),
126
+ }, value: text, onUpdate: (v) => {
127
+ setText(v);
128
+ }, autoFocus: true }),
129
+ React.createElement("div", { className: b('controls') },
130
+ React.createElement("div", null,
131
+ React.createElement(Button, { onClick: onCancel, view: 'flat' },
132
+ React.createElement("span", { className: cnHelper({ 'prosemirror-stop-event': true }) }, i18n('cancel'))),
133
+ React.createElement(Button, { onClick: () => onSave(text), view: 'action' },
134
+ React.createElement("span", { className: cnHelper({ 'prosemirror-stop-event': true }) }, i18n('save'))))))));
135
+ };
136
+ export const YfmHtmlBlockView = ({ onChange, node, getPos, view, useConfig }) => {
137
+ const [editing, setEditing, unsetEditing, toggleEditing] = useBooleanState(Boolean(node.attrs[YfmHtmlBlockConsts.NodeAttrs.newCreated]));
138
+ const config = useConfig === null || useConfig === void 0 ? void 0 : useConfig();
139
+ const [menuOpen, , , toggleMenuOpen] = useBooleanState(false);
140
+ const buttonRef = useRef(null);
141
+ const handleClick = () => {
142
+ setEditing();
143
+ };
144
+ if (editing) {
145
+ return (React.createElement(CodeEditMode, { initialText: node.attrs[YfmHtmlBlockConsts.NodeAttrs.srcdoc], onCancel: unsetEditing, onSave: (v) => {
146
+ onChange({ [YfmHtmlBlockConsts.NodeAttrs.srcdoc]: v });
147
+ unsetEditing();
148
+ } }));
149
+ }
150
+ return (React.createElement("div", { className: b(), onDoubleClick: setEditing },
151
+ React.createElement(Label, { className: b('label'), icon: React.createElement(Icon, { size: 16, data: Eye }) }, i18n('preview')),
152
+ React.createElement(YfmHtmlBlockPreview, { html: node.attrs[YfmHtmlBlockConsts.NodeAttrs.srcdoc], "on\u0421lick": handleClick, config: config }),
153
+ React.createElement("div", { className: b('menu') },
154
+ React.createElement(Button, { onClick: toggleMenuOpen, ref: buttonRef, size: 's', className: cnHelper({ 'prosemirror-stop-event': true }) },
155
+ React.createElement(Icon, { data: DotsIcon, className: cnHelper({ 'prosemirror-stop-event': true }) })),
156
+ React.createElement(Popup, { anchorRef: buttonRef, open: menuOpen, onClose: toggleMenuOpen, placement: ['bottom-end'] },
157
+ React.createElement(Menu, null,
158
+ React.createElement(Menu.Item, { onClick: () => {
159
+ toggleEditing();
160
+ toggleMenuOpen();
161
+ } }, i18n('edit')),
162
+ React.createElement(Menu.Item, { onClick: () => {
163
+ const pos = getPos();
164
+ if (pos === undefined)
165
+ return;
166
+ removeNode({
167
+ node,
168
+ pos,
169
+ tr: view.state.tr,
170
+ dispatch: view.dispatch,
171
+ });
172
+ } }, i18n('remove')))))));
173
+ };
@@ -0,0 +1 @@
1
+ export * from './NodeView';
@@ -0,0 +1 @@
1
+ export * from './NodeView';
@@ -0,0 +1,15 @@
1
+ export declare enum YfmHtmlBlockAttrs {
2
+ class = "class",
3
+ frameborder = "frameborder",
4
+ newCreated = "newCreated",
5
+ srcdoc = "srcdoc",
6
+ style = "style"
7
+ }
8
+ export declare const yfmHtmlBlockNodeName = "yfm_html_block";
9
+ export declare const yfmHtmlBlockNodeType: (schema: import("prosemirror-model").Schema<any, any>) => import("prosemirror-model").NodeType;
10
+ export declare const YfmHtmlBlockAction = "createYfmHtmlBlock";
11
+ export declare const YfmHtmlBlockConsts: {
12
+ readonly NodeName: "yfm_html_block";
13
+ readonly NodeAttrs: typeof YfmHtmlBlockAttrs;
14
+ readonly nodeType: (schema: import("prosemirror-model").Schema<any, any>) => import("prosemirror-model").NodeType;
15
+ };
@@ -0,0 +1,17 @@
1
+ import { nodeTypeFactory } from '../../../../utils/schema';
2
+ export var YfmHtmlBlockAttrs;
3
+ (function (YfmHtmlBlockAttrs) {
4
+ YfmHtmlBlockAttrs["class"] = "class";
5
+ YfmHtmlBlockAttrs["frameborder"] = "frameborder";
6
+ YfmHtmlBlockAttrs["newCreated"] = "newCreated";
7
+ YfmHtmlBlockAttrs["srcdoc"] = "srcdoc";
8
+ YfmHtmlBlockAttrs["style"] = "style";
9
+ })(YfmHtmlBlockAttrs || (YfmHtmlBlockAttrs = {}));
10
+ export const yfmHtmlBlockNodeName = 'yfm_html_block';
11
+ export const yfmHtmlBlockNodeType = nodeTypeFactory(yfmHtmlBlockNodeName);
12
+ export const YfmHtmlBlockAction = 'createYfmHtmlBlock';
13
+ export const YfmHtmlBlockConsts = {
14
+ NodeName: yfmHtmlBlockNodeName,
15
+ NodeAttrs: YfmHtmlBlockAttrs,
16
+ nodeType: yfmHtmlBlockNodeType,
17
+ };
@@ -0,0 +1,10 @@
1
+ import type { ExtensionNodeSpec } from '../../../../core';
2
+ export { yfmHtmlBlockNodeName } from './const';
3
+ export declare type YfmHtmlBlockSpecsOptions = {
4
+ nodeView?: ExtensionNodeSpec['view'];
5
+ };
6
+ export declare const YfmHtmlBlockSpecs: import("../../../../core").ExtensionWithOptions<YfmHtmlBlockSpecsOptions> & {
7
+ readonly NodeName: "yfm_html_block";
8
+ readonly NodeAttrs: typeof import("./const").YfmHtmlBlockAttrs;
9
+ readonly nodeType: (schema: import("prosemirror-model").Schema<any, any>) => import("prosemirror-model").NodeType;
10
+ };
@@ -0,0 +1,39 @@
1
+ // eslint-disable-next-line import/no-extraneous-dependencies
2
+ import { transform } from '@diplodoc/html-extension';
3
+ import { YfmHtmlBlockConsts } from './const';
4
+ export { yfmHtmlBlockNodeName } from './const';
5
+ const YfmHtmlBlockSpecsExtension = (builder, { nodeView }) => {
6
+ builder
7
+ .configureMd((md) => md.use(transform({ bundle: false }), {}))
8
+ .addNode(YfmHtmlBlockConsts.NodeName, () => ({
9
+ fromMd: {
10
+ tokenSpec: {
11
+ name: YfmHtmlBlockConsts.NodeName,
12
+ type: 'node',
13
+ noCloseToken: true,
14
+ getAttrs: (token) => { var _a; return Object.fromEntries((_a = token.attrs) !== null && _a !== void 0 ? _a : []); },
15
+ },
16
+ },
17
+ spec: {
18
+ group: 'block',
19
+ attrs: {
20
+ [YfmHtmlBlockConsts.NodeAttrs.class]: { default: 'yfm-html' },
21
+ [YfmHtmlBlockConsts.NodeAttrs.frameborder]: { default: '' },
22
+ [YfmHtmlBlockConsts.NodeAttrs.srcdoc]: { default: '' },
23
+ [YfmHtmlBlockConsts.NodeAttrs.style]: { default: null },
24
+ [YfmHtmlBlockConsts.NodeAttrs.newCreated]: { default: null },
25
+ },
26
+ toDOM: (node) => ['iframe', node.attrs],
27
+ },
28
+ toMd: (state, node) => {
29
+ state.write('::: html');
30
+ state.write('\n');
31
+ state.write(node.attrs[YfmHtmlBlockConsts.NodeAttrs.srcdoc]);
32
+ state.ensureNewLine();
33
+ state.write(':::');
34
+ state.closeBlock(node);
35
+ },
36
+ view: nodeView,
37
+ }));
38
+ };
39
+ export const YfmHtmlBlockSpecs = Object.assign(YfmHtmlBlockSpecsExtension, YfmHtmlBlockConsts);
@@ -0,0 +1,2 @@
1
+ import { ActionSpec } from '../../../core';
2
+ export declare const addYfmHtmlBlock: ActionSpec;
@@ -0,0 +1,12 @@
1
+ import { YfmHtmlBlockConsts, yfmHtmlBlockNodeType } from './YfmHtmlBlockSpecs/const';
2
+ export const addYfmHtmlBlock = {
3
+ isEnable(state) {
4
+ return state.selection.empty;
5
+ },
6
+ run(state, dispatch, _view) {
7
+ dispatch(state.tr.insert(state.selection.from, yfmHtmlBlockNodeType(state.schema).create({
8
+ [YfmHtmlBlockConsts.NodeAttrs.srcdoc]: '\n',
9
+ [YfmHtmlBlockConsts.NodeAttrs.newCreated]: true,
10
+ })));
11
+ },
12
+ };
@@ -0,0 +1,2 @@
1
+ export * from './YfmHtmlBlockSpecs/const';
2
+ export declare const YfmHtmlBlockAction = "createYfmHtmlBlock";
@@ -0,0 +1,2 @@
1
+ export * from './YfmHtmlBlockSpecs/const';
2
+ export const YfmHtmlBlockAction = 'createYfmHtmlBlock';
@@ -0,0 +1,14 @@
1
+ import type { IHTMLIFrameElementConfig } from '@diplodoc/html-extension/runtime';
2
+ import { Action, ExtensionAuto } from '../../../core';
3
+ import { YfmHtmlBlockAction } from './YfmHtmlBlockSpecs/const';
4
+ export declare type YfmHtmlBlockOptions = {
5
+ useConfig?: () => IHTMLIFrameElementConfig | undefined;
6
+ };
7
+ export declare const YfmHtmlBlock: ExtensionAuto<YfmHtmlBlockOptions>;
8
+ declare global {
9
+ namespace WysiwygEditor {
10
+ interface Actions {
11
+ [YfmHtmlBlockAction]: Action;
12
+ }
13
+ }
14
+ }
@@ -0,0 +1,13 @@
1
+ import { WYfmHtmlBlockNodeView } from './YfmHtmlBlockNodeView';
2
+ import { YfmHtmlBlockSpecs } from './YfmHtmlBlockSpecs';
3
+ import { YfmHtmlBlockAction } from './YfmHtmlBlockSpecs/const';
4
+ import { addYfmHtmlBlock } from './actions';
5
+ export const YfmHtmlBlock = (builder, options) => {
6
+ builder.use(YfmHtmlBlockSpecs, {
7
+ nodeView: YfmHtmlBlockNodeViewFactory(options),
8
+ });
9
+ builder.addAction(YfmHtmlBlockAction, () => addYfmHtmlBlock);
10
+ };
11
+ const YfmHtmlBlockNodeViewFactory = (options) => () => (node, view, getPos) => {
12
+ return new WYfmHtmlBlockNodeView({ node, view, getPos, options });
13
+ };
@@ -4,8 +4,8 @@ export * from './Emoji';
4
4
  export * from './ImgSize';
5
5
  export * from './Monospace';
6
6
  export * from './Video';
7
- export * from './YfmCut';
8
7
  export * from './YfmConfigs';
8
+ export * from './YfmCut';
9
9
  export * from './YfmFile';
10
10
  export * from './YfmHeading';
11
11
  export * from './YfmNote';
@@ -4,8 +4,8 @@ export * from './Emoji';
4
4
  export * from './ImgSize';
5
5
  export * from './Monospace';
6
6
  export * from './Video';
7
- export * from './YfmCut';
8
7
  export * from './YfmConfigs';
8
+ export * from './YfmCut';
9
9
  export * from './YfmFile';
10
10
  export * from './YfmHeading';
11
11
  export * from './YfmNote';
@@ -1,5 +1,9 @@
1
1
  {
2
- "toolbar_action_disabled": "Incompatible markup element",
2
+ "cancel": "Cancel",
3
+ "delete": "Delete",
3
4
  "edit": "Edit",
4
- "delete": "Delete"
5
+ "preview": "Preview",
6
+ "remove": "Remove",
7
+ "save": "Save",
8
+ "toolbar_action_disabled": "Incompatible markup element"
5
9
  }