@gravity-ui/markdown-editor 13.16.0 → 13.17.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.
Files changed (48) hide show
  1. package/build/cjs/extensions/behavior/Resizable/Resizable.css +38 -0
  2. package/build/cjs/extensions/behavior/Resizable/Resizable.d.ts +9 -0
  3. package/build/cjs/extensions/behavior/Resizable/Resizable.js +14 -0
  4. package/build/cjs/extensions/markdown/Table/plugins/TableCellContextPlugin/index.js +19 -9
  5. package/build/cjs/extensions/yfm/ImgSize/plugins/ImgSizeNodeView/ImgSettingsButton.css +6 -0
  6. package/build/cjs/extensions/yfm/ImgSize/plugins/ImgSizeNodeView/ImgSettingsButton.d.ts +6 -1
  7. package/build/cjs/extensions/yfm/ImgSize/plugins/ImgSizeNodeView/ImgSettingsButton.js +21 -37
  8. package/build/cjs/extensions/yfm/ImgSize/plugins/ImgSizeNodeView/NodeView.d.ts +1 -1
  9. package/build/cjs/extensions/yfm/ImgSize/plugins/ImgSizeNodeView/NodeView.js +62 -4
  10. package/build/cjs/extensions/yfm/Mermaid/MermaidNodeView/MermaidView.js +8 -7
  11. package/build/cjs/extensions/yfm/YfmHtmlBlock/YfmHtmlBlockNodeView/YfmHtmlBlockView.js +31 -9
  12. package/build/cjs/extensions/yfm/YfmHtmlBlock/YfmHtmlBlockSpecs/index.d.ts +1 -1
  13. package/build/cjs/extensions/yfm/YfmHtmlBlock/YfmHtmlBlockSpecs/index.js +2 -2
  14. package/build/cjs/extensions/yfm/YfmHtmlBlock/index.d.ts +1 -1
  15. package/build/cjs/i18n/yfm-table/en.json +10 -1
  16. package/build/cjs/i18n/yfm-table/index.d.ts +10 -1
  17. package/build/cjs/i18n/yfm-table/ru.json +10 -1
  18. package/build/cjs/react-utils/useNodeEditing.d.ts +3 -2
  19. package/build/cjs/react-utils/useNodeEditing.js +1 -1
  20. package/build/cjs/react-utils/useNodeResizing.d.ts +22 -0
  21. package/build/cjs/react-utils/useNodeResizing.js +82 -0
  22. package/build/cjs/version.js +1 -1
  23. package/build/cjs/view/hocs/withYfmHtml/index.js +2 -2
  24. package/build/esm/extensions/behavior/Resizable/Resizable.css +38 -0
  25. package/build/esm/extensions/behavior/Resizable/Resizable.d.ts +10 -0
  26. package/build/esm/extensions/behavior/Resizable/Resizable.js +10 -0
  27. package/build/esm/extensions/markdown/Table/plugins/TableCellContextPlugin/index.js +19 -9
  28. package/build/esm/extensions/yfm/ImgSize/plugins/ImgSizeNodeView/ImgSettingsButton.css +6 -0
  29. package/build/esm/extensions/yfm/ImgSize/plugins/ImgSizeNodeView/ImgSettingsButton.d.ts +7 -1
  30. package/build/esm/extensions/yfm/ImgSize/plugins/ImgSizeNodeView/ImgSettingsButton.js +23 -38
  31. package/build/esm/extensions/yfm/ImgSize/plugins/ImgSizeNodeView/NodeView.d.ts +1 -1
  32. package/build/esm/extensions/yfm/ImgSize/plugins/ImgSizeNodeView/NodeView.js +63 -5
  33. package/build/esm/extensions/yfm/Mermaid/MermaidNodeView/MermaidView.js +5 -4
  34. package/build/esm/extensions/yfm/YfmHtmlBlock/YfmHtmlBlockNodeView/YfmHtmlBlockView.js +31 -9
  35. package/build/esm/extensions/yfm/YfmHtmlBlock/YfmHtmlBlockSpecs/index.d.ts +1 -1
  36. package/build/esm/extensions/yfm/YfmHtmlBlock/YfmHtmlBlockSpecs/index.js +2 -2
  37. package/build/esm/extensions/yfm/YfmHtmlBlock/index.d.ts +1 -1
  38. package/build/esm/i18n/yfm-table/en.json +10 -1
  39. package/build/esm/i18n/yfm-table/index.d.ts +10 -1
  40. package/build/esm/i18n/yfm-table/ru.json +10 -1
  41. package/build/esm/react-utils/useNodeEditing.d.ts +3 -2
  42. package/build/esm/react-utils/useNodeEditing.js +1 -1
  43. package/build/esm/react-utils/useNodeResizing.d.ts +22 -0
  44. package/build/esm/react-utils/useNodeResizing.js +77 -0
  45. package/build/esm/version.js +1 -1
  46. package/build/esm/view/hocs/withYfmHtml/index.js +3 -3
  47. package/build/styles.css +44 -0
  48. package/package.json +4 -4
@@ -9,13 +9,13 @@ function withYfmHtmlBlock(opts) {
9
9
  return (Component) => (0, react_1.forwardRef)(function WithYfmHtml(props, ref) {
10
10
  const { meta, html, yfmHtmlBlockConfig } = props;
11
11
  (0, useYfmHtmlBlockRuntime_1.useYfmHtmlBlockRuntime)(meta, opts.runtime);
12
- const yfmHtmlBlock = (0, react_2.useDiplodocHtml)();
12
+ const yfmHtmlBlock = (0, react_2.useDiplodocEmbeddedContentController)();
13
13
  (0, react_1.useEffect)(() => {
14
14
  if (yfmHtmlBlock) {
15
15
  if (yfmHtmlBlockConfig) {
16
16
  yfmHtmlBlock.setConfig(yfmHtmlBlockConfig);
17
17
  }
18
- yfmHtmlBlock.reinitialize();
18
+ yfmHtmlBlock.initialize();
19
19
  }
20
20
  }, [yfmHtmlBlock, html, yfmHtmlBlockConfig]);
21
21
  return react_1.default.createElement(Component, Object.assign({}, props, { ref: ref }));
@@ -0,0 +1,38 @@
1
+ body :has(.g-md-resizable_resizing) {
2
+ cursor: col-resize;
3
+ }
4
+
5
+ .g-md-resizable {
6
+ position: relative;
7
+ }
8
+ .g-md-resizable_resizing .g-md-resizable__resizer-wrapper, .g-md-resizable_hover .g-md-resizable__resizer-wrapper {
9
+ position: absolute;
10
+ z-index: 1;
11
+ top: 0;
12
+ display: flex;
13
+ justify-content: center;
14
+ align-items: center;
15
+ width: 20px;
16
+ height: 100%;
17
+ cursor: col-resize;
18
+ pointer-events: auto;
19
+ }
20
+ .g-md-resizable_resizing .g-md-resizable__resizer-wrapper_left, .g-md-resizable_hover .g-md-resizable__resizer-wrapper_left {
21
+ left: 0;
22
+ }
23
+ .g-md-resizable_resizing .g-md-resizable__resizer-wrapper_right, .g-md-resizable_hover .g-md-resizable__resizer-wrapper_right {
24
+ right: 0;
25
+ }
26
+ .g-md-resizable__resizer {
27
+ opacity: 0;
28
+ }
29
+ .g-md-resizable_resizing .g-md-resizable__resizer, .g-md-resizable_hover .g-md-resizable__resizer {
30
+ box-sizing: content-box;
31
+ width: 4px;
32
+ height: 50px;
33
+ max-height: 50%;
34
+ opacity: 1;
35
+ border-radius: 6px;
36
+ background: rgba(127, 127, 127, 0.8);
37
+ transition: opacity 300ms ease-in 0s;
38
+ }
@@ -0,0 +1,10 @@
1
+ import React from 'react';
2
+ import './Resizable.css';
3
+ export interface ResizableProps {
4
+ children: React.ReactNode;
5
+ onResizeLeft: (event: React.MouseEvent<HTMLElement>) => void;
6
+ onResizeRight: (event: React.MouseEvent<HTMLElement>) => void;
7
+ hover?: boolean;
8
+ resizing?: boolean;
9
+ }
10
+ export declare const Resizable: React.FC<ResizableProps>;
@@ -0,0 +1,10 @@
1
+ import React from 'react';
2
+ import { cn } from '../../../classname';
3
+ import './Resizable.css';
4
+ const b = cn('resizable');
5
+ const Resizer = ({ onMouseDown, direction }) => (React.createElement("div", { className: b('resizer-wrapper', { [direction]: true }), role: "button", tabIndex: 0, onMouseDown: onMouseDown },
6
+ React.createElement("div", { className: b('resizer') })));
7
+ export const Resizable = ({ hover, resizing, children, onResizeLeft, onResizeRight, }) => (React.createElement("div", { className: b({ hover, resizing }) },
8
+ children,
9
+ React.createElement(Resizer, { onMouseDown: onResizeLeft, direction: "left" }),
10
+ React.createElement(Resizer, { onMouseDown: onResizeRight, direction: "right" })));
@@ -1,5 +1,6 @@
1
1
  import { Plugin } from 'prosemirror-state';
2
2
  import { bindActions } from '../../../../../core';
3
+ import { i18n } from '../../../../../i18n/yfm-table';
3
4
  import { convertToYfmTable } from '../../../../yfm';
4
5
  import { TableNode } from '../../TableSpecs';
5
6
  import { innerActions } from '../../actions/innerActions';
@@ -14,18 +15,27 @@ export const tableCellContextPlugin = () => new Plugin({
14
15
  } }))(view);
15
16
  return new TableCellContextView(view, [
16
17
  [
17
- { action: actions.setCellLeftAlign, text: 'left' },
18
- { action: actions.setCellCenterAlign, text: 'center' },
19
- { action: actions.setCellRightAlign, text: 'right' },
18
+ {
19
+ action: actions.setCellLeftAlign,
20
+ text: i18n('table.menu.cell.align.left'),
21
+ },
22
+ {
23
+ action: actions.setCellCenterAlign,
24
+ text: i18n('table.menu.cell.align.center'),
25
+ },
26
+ {
27
+ action: actions.setCellRightAlign,
28
+ text: i18n('table.menu.cell.align.right'),
29
+ },
20
30
  ],
21
31
  [
22
- { action: actions.addRow, text: 'add row' },
23
- { action: actions.deleteRow, text: 'del row' },
24
- { action: actions.addColumn, text: 'add column' },
25
- { action: actions.deleteColumn, text: 'del column' },
32
+ { action: actions.addRow, text: i18n('table.menu.row.add') },
33
+ { action: actions.deleteRow, text: i18n('table.menu.row.remove') },
34
+ { action: actions.addColumn, text: i18n('table.menu.column.add') },
35
+ { action: actions.deleteColumn, text: i18n('table.menu.column.remove') },
26
36
  ],
27
- { action: actions.convert, text: 'convert to yfm table' },
28
- { action: actions.deleteTable, text: 'del table' },
37
+ { action: actions.convert, text: i18n('table.menu.convert.yfm') },
38
+ { action: actions.deleteTable, text: i18n('table.menu.table.remove') },
29
39
  ], [schema.nodes[TableNode.HeaderCell], schema.nodes[TableNode.DataCell]]);
30
40
  },
31
41
  });
@@ -0,0 +1,6 @@
1
+ .g-md-img-settings-button {
2
+ position: absolute;
3
+ z-index: 2;
4
+ top: 3px;
5
+ right: 3px;
6
+ }
@@ -1,10 +1,16 @@
1
1
  import React, { RefObject } from 'react';
2
2
  import { Node } from 'prosemirror-model';
3
3
  import { EditorView } from 'prosemirror-view';
4
+ import './ImgSettingsButton.css';
4
5
  export declare const ImgSettingsButton: React.FC<{
5
6
  node: Node;
6
7
  view: EditorView;
7
8
  getPos: () => number | undefined;
8
- nodeRef: RefObject<HTMLElement>;
9
9
  updateAttributes: (o: object) => void;
10
+ nodeRef: RefObject<HTMLDivElement>;
11
+ visible: boolean;
12
+ toggleEdit: () => void;
13
+ edit: boolean;
14
+ unsetEdit: () => void;
15
+ onDelete: () => void;
10
16
  }>;
@@ -1,48 +1,33 @@
1
- import React, { useEffect, useRef } from 'react';
1
+ import React, { useRef } from 'react';
2
2
  import { Ellipsis } from '@gravity-ui/icons';
3
3
  import { Button, Icon, Menu, Popup } from '@gravity-ui/uikit';
4
+ import { cn } from '../../../../../classname';
4
5
  import { i18n as i18nCommon } from '../../../../../i18n/common';
5
6
  import { useBooleanState } from '../../../../../react-utils/hooks';
6
- import { useNodeEditing } from '../../../../../react-utils/useNodeEditing';
7
- import { useNodeHovered } from '../../../../../react-utils/useNodeHovered';
8
- import { removeNode } from '../../../../../utils/remove-node';
9
- import { imageRendererKey } from '../../const';
10
7
  import { ImageForm } from './ImageForm';
11
- export const ImgSettingsButton = function ({ node, view, getPos, nodeRef, updateAttributes }) {
8
+ import './ImgSettingsButton.css';
9
+ const b = cn('img-settings-button');
10
+ export const ImgSettingsButton = function ({ node, view, updateAttributes, visible, edit, toggleEdit, nodeRef, unsetEdit, onDelete, }) {
12
11
  const [popupOpen, setPopupOpen, unsetPopupOpen] = useBooleanState(false);
13
12
  const placement = ['bottom-end', 'bottom-start'];
14
13
  const buttonRef = useRef(null);
15
- const isNodeHovered = useNodeHovered(nodeRef);
16
- const isButtonHovered = useNodeHovered(buttonRef);
17
- const [edit, setEditing, unsetEdit, toggleEdit] = useNodeEditing({ nodeRef, view });
18
- const visible = (isNodeHovered || isButtonHovered || popupOpen) && !edit;
19
- useEffect(() => {
20
- var _a;
21
- if ((_a = imageRendererKey.getState(view.state)) === null || _a === void 0 ? void 0 : _a.linkAdded) {
22
- setEditing();
23
- }
24
- }, [view, setEditing]);
25
- if (edit)
26
- return (React.createElement(ImageForm, { node: node, view: view, updateAttributes: updateAttributes, dom: nodeRef, unsetEdit: unsetEdit }));
27
- return visible ? (React.createElement(React.Fragment, null,
28
- React.createElement(Button, { onClick: setPopupOpen, ref: buttonRef, size: "s", view: 'raised', style: { position: 'absolute', right: '3px', top: '3px' } },
29
- React.createElement(Icon, { data: Ellipsis })),
30
- React.createElement(Popup, { open: popupOpen, anchorRef: buttonRef, onClose: unsetPopupOpen, placement: placement },
14
+ const handleEdit = () => {
15
+ toggleEdit();
16
+ unsetPopupOpen();
17
+ };
18
+ const isVisibleImageForm = edit;
19
+ const isVisibleEditButton = !edit && (visible || popupOpen);
20
+ const isVisiblePopup = !edit && popupOpen;
21
+ const handleEditButtonClick = (event) => {
22
+ event.preventDefault();
23
+ setPopupOpen();
24
+ };
25
+ return (React.createElement(React.Fragment, null,
26
+ isVisibleImageForm && (React.createElement(ImageForm, { node: node, view: view, updateAttributes: updateAttributes, dom: nodeRef, unsetEdit: unsetEdit })),
27
+ isVisibleEditButton && (React.createElement(Button, { onClick: handleEditButtonClick, ref: buttonRef, size: "s", view: 'raised', className: b() },
28
+ React.createElement(Icon, { data: Ellipsis }))),
29
+ React.createElement(Popup, { open: isVisiblePopup, anchorRef: buttonRef, onClose: unsetPopupOpen, placement: placement },
31
30
  React.createElement(Menu, null,
32
- React.createElement(Menu.Item, { onClick: () => {
33
- toggleEdit();
34
- unsetPopupOpen();
35
- } }, i18nCommon('edit')),
36
- React.createElement(Menu.Item, { onClick: () => {
37
- const pos = getPos();
38
- if (pos === undefined)
39
- return;
40
- removeNode({
41
- node,
42
- pos,
43
- tr: view.state.tr,
44
- dispatch: view.dispatch,
45
- });
46
- view.focus();
47
- } }, i18nCommon('delete')))))) : null;
31
+ React.createElement(Menu.Item, { onClick: handleEdit }, i18nCommon('edit')),
32
+ React.createElement(Menu.Item, { onClick: onDelete }, i18nCommon('delete'))))));
48
33
  };
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
- import { ReactNodeViewProps } from '../../../../../react-utils/react-node-view';
2
+ import { ReactNodeViewProps } from '../../../../../react-utils';
3
3
  import './ImgNodeView.css';
4
4
  export declare const cnImgSizeNodeView: import("@bem-react/classname").ClassNameFormatter;
5
5
  export declare const ImageNodeView: React.FC<ReactNodeViewProps>;
@@ -1,11 +1,69 @@
1
- import React, { useRef } from 'react';
1
+ import React, { useCallback, useEffect, useRef } from 'react';
2
2
  import { cn } from '../../../../../classname';
3
+ import { useNodeEditing, useNodeHovered } from '../../../../../react-utils';
4
+ import { useNodeResizing } from '../../../../../react-utils/useNodeResizing';
5
+ import { removeNode } from '../../../../../utils';
6
+ import { Resizable } from '../../../../behavior/Resizable/Resizable';
7
+ import { ImgSizeAttr } from '../../ImgSizeSpecs';
8
+ import { imageRendererKey } from '../../const';
3
9
  import { ImgSettingsButton } from './ImgSettingsButton';
4
10
  import './ImgNodeView.css';
5
11
  export const cnImgSizeNodeView = cn('img-size-node-view');
6
12
  export const ImageNodeView = ({ node, view, getPos, updateAttributes, }) => {
7
- const ref = useRef(null);
8
- return (React.createElement(React.Fragment, null,
9
- React.createElement(ImgSettingsButton, { node: node, view: view, getPos: getPos, updateAttributes: updateAttributes, nodeRef: ref }),
10
- React.createElement("img", Object.assign({}, node.attrs, { ref: ref }))));
13
+ const imageContainerRef = useRef(null);
14
+ const imageRef = useRef(null);
15
+ const alt = node.attrs[ImgSizeAttr.Alt] || '';
16
+ const initialHeight = node.attrs[ImgSizeAttr.Height];
17
+ const initialWidth = node.attrs[ImgSizeAttr.Width];
18
+ const src = node.attrs[ImgSizeAttr.Src] || '';
19
+ const title = node.attrs[ImgSizeAttr.Title] || '';
20
+ const isNodeHovered = useNodeHovered(imageContainerRef);
21
+ const [edit, setEditing, unsetEdit, toggleEdit] = useNodeEditing({
22
+ nodeRef: imageContainerRef,
23
+ view,
24
+ });
25
+ const handleResize = useCallback(({ width, height }) => {
26
+ updateAttributes({
27
+ width: width === undefined ? undefined : String(Math.round(width)),
28
+ height: height === undefined ? undefined : String(Math.round(height)),
29
+ name: title,
30
+ alt,
31
+ });
32
+ }, [alt, title, updateAttributes]);
33
+ const { state, startResizing } = useNodeResizing({
34
+ width: initialWidth,
35
+ height: initialHeight,
36
+ ref: imageRef,
37
+ onResize: handleResize,
38
+ });
39
+ const style = {
40
+ width: state.width ? `${state.width}px` : '',
41
+ height: state.height ? `${state.height}px` : '',
42
+ transition: 'width 0.15s ease-out, height 0.15s ease-out',
43
+ };
44
+ const handleDelete = useCallback(() => {
45
+ const pos = getPos();
46
+ if (pos === undefined)
47
+ return;
48
+ removeNode({
49
+ node,
50
+ pos,
51
+ tr: view.state.tr,
52
+ dispatch: view.dispatch,
53
+ });
54
+ view.focus();
55
+ }, [getPos, node, view]);
56
+ const createHandleResize = (direction) => (event) => {
57
+ startResizing(event, direction);
58
+ };
59
+ useEffect(() => {
60
+ var _a;
61
+ if ((_a = imageRendererKey.getState(view.state)) === null || _a === void 0 ? void 0 : _a.linkAdded) {
62
+ setEditing();
63
+ }
64
+ }, [view, setEditing]);
65
+ return (React.createElement("div", { ref: imageContainerRef },
66
+ React.createElement(Resizable, { hover: isNodeHovered, resizing: state.resizing, onResizeLeft: createHandleResize('left'), onResizeRight: createHandleResize('right') },
67
+ React.createElement(ImgSettingsButton, { node: node, view: view, getPos: getPos, updateAttributes: updateAttributes, visible: isNodeHovered && !edit && !state.resizing, edit: edit, toggleEdit: toggleEdit, nodeRef: imageRef, onDelete: handleDelete, unsetEdit: unsetEdit }),
68
+ React.createElement("img", { ref: imageRef, src: src, alt: alt, style: style }))));
11
69
  };
@@ -3,8 +3,9 @@ import { Ellipsis as DotsIcon } from '@gravity-ui/icons';
3
3
  import { Button, Icon, Loader, Menu, Popup } from '@gravity-ui/uikit';
4
4
  import { cn } from '../../../../classname';
5
5
  import { TextAreaFixed as TextArea } from '../../../../forms/TextInput';
6
- import { useBooleanState } from '../../../../react-utils/hooks';
7
- import { removeNode } from '../../../../utils/remove-node';
6
+ import { i18n } from '../../../../i18n/common';
7
+ import { useBooleanState } from '../../../../react-utils';
8
+ import { removeNode } from '../../../../utils';
8
9
  import { MermaidConsts } from '../MermaidSpecs/const';
9
10
  export const cnMermaid = cn('Mermaid');
10
11
  export const cnDiagramHelper = cn('MermaidHelper');
@@ -85,7 +86,7 @@ export const MermaidView = ({ onChange, node, getPos, view, getMermaidInstance }
85
86
  React.createElement(Menu.Item, { onClick: () => {
86
87
  toggleEditing();
87
88
  toggleMenuOpen();
88
- } }, "Edit"),
89
+ } }, i18n('edit')),
89
90
  React.createElement(Menu.Item, { onClick: () => {
90
91
  const pos = getPos();
91
92
  if (pos === undefined)
@@ -96,5 +97,5 @@ export const MermaidView = ({ onChange, node, getPos, view, getMermaidInstance }
96
97
  tr: view.state.tr,
97
98
  dispatch: view.dispatch,
98
99
  });
99
- } }, "Remove"))))));
100
+ } }, i18n('remove')))))));
100
101
  };
@@ -18,6 +18,16 @@ export function generateID() {
18
18
  }
19
19
  const DEFAULT_PADDING = 20;
20
20
  const DEFAULT_DELAY = 100;
21
+ const createLinkCLickHandler = (value, document) => (event) => {
22
+ event.preventDefault();
23
+ const targetId = value.getAttribute('href');
24
+ if (targetId) {
25
+ const targetElement = document.querySelector(targetId);
26
+ if (targetElement) {
27
+ targetElement.scrollIntoView({ behavior: 'smooth' });
28
+ }
29
+ }
30
+ };
21
31
  const YfmHtmlBlockPreview = ({ html, onСlick, config }) => {
22
32
  var _a, _b, _c, _d, _e, _f;
23
33
  const ref = useRef(null);
@@ -26,11 +36,6 @@ const YfmHtmlBlockPreview = ({ html, onСlick, config }) => {
26
36
  const resizeConfig = useRef({});
27
37
  const [height, setHeight] = useState('100%');
28
38
  useEffect(() => {
29
- var _a, _b;
30
- resizeConfig.current = {
31
- padding: (_a = config === null || config === void 0 ? void 0 : config.resizePadding) !== null && _a !== void 0 ? _a : DEFAULT_PADDING,
32
- delay: (_b = config === null || config === void 0 ? void 0 : config.resizeDelay) !== null && _b !== void 0 ? _b : DEFAULT_DELAY,
33
- };
34
39
  setStyles(config === null || config === void 0 ? void 0 : config.styles);
35
40
  setClassNames(config === null || config === void 0 ? void 0 : config.classNames);
36
41
  }, [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]);
@@ -99,18 +104,35 @@ const YfmHtmlBlockPreview = ({ html, onСlick, config }) => {
99
104
  styles.current = newStyles;
100
105
  }
101
106
  };
102
- useEffect(() => {
107
+ // finds all relative links (href^="#") and changes their click behavior
108
+ const createAnchorLinkHandlers = (type) => () => {
103
109
  var _a;
110
+ const document = (_a = ref.current) === null || _a === void 0 ? void 0 : _a.contentWindow.document;
111
+ if (document) {
112
+ document.querySelectorAll('a[href^="#"]').forEach((value) => {
113
+ const handler = createLinkCLickHandler(value, document);
114
+ if (type === 'add') {
115
+ value.addEventListener('click', handler);
116
+ }
117
+ else {
118
+ value.removeEventListener('click', handler);
119
+ }
120
+ });
121
+ }
122
+ };
123
+ useEffect(() => {
124
+ var _a, _b;
104
125
  (_a = ref.current) === null || _a === void 0 ? void 0 : _a.addEventListener('load', handleLoadIFrame);
126
+ (_b = ref.current) === null || _b === void 0 ? void 0 : _b.addEventListener('load', createAnchorLinkHandlers('add'));
105
127
  return () => {
106
- var _a;
128
+ var _a, _b;
107
129
  (_a = ref.current) === null || _a === void 0 ? void 0 : _a.removeEventListener('load', handleLoadIFrame);
130
+ (_b = ref.current) === null || _b === void 0 ? void 0 : _b.removeEventListener('load', createAnchorLinkHandlers('remove'));
108
131
  };
109
132
  }, [html]);
110
133
  useEffect(() => {
111
- var _a, _b;
112
134
  if (ref.current) {
113
- 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));
135
+ const resizeObserver = new window.ResizeObserver(debounce(handleResizeIFrame, DEFAULT_DELAY));
114
136
  resizeObserver.observe(ref.current);
115
137
  }
116
138
  }, [(_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]);
@@ -1,7 +1,7 @@
1
1
  import { PluginOptions } from '@diplodoc/html-extension/plugin/transform';
2
2
  import type { ExtensionNodeSpec } from '../../../../core';
3
3
  export { yfmHtmlBlockNodeName } from './const';
4
- export interface YfmHtmlBlockSpecsOptions extends Omit<PluginOptions, 'runtimeJsPath' | 'containerClasses' | 'bundle'> {
4
+ export interface YfmHtmlBlockSpecsOptions extends Omit<PluginOptions, 'runtimeJsPath' | 'containerClasses' | 'bundle' | 'embeddingMode'> {
5
5
  nodeView?: ExtensionNodeSpec['view'];
6
6
  }
7
7
  export declare const YfmHtmlBlockSpecs: import("../../../../core").ExtensionWithOptions<YfmHtmlBlockSpecsOptions> & {
@@ -6,14 +6,14 @@ export { yfmHtmlBlockNodeName } from './const';
6
6
  const YfmHtmlBlockSpecsExtension = (builder, _a) => {
7
7
  var { nodeView } = _a, options = __rest(_a, ["nodeView"]);
8
8
  builder
9
- .configureMd((md) => md.use(transform(Object.assign({ bundle: false }, options)), {}))
9
+ .configureMd((md) => md.use(transform(Object.assign({ bundle: false, embeddingMode: 'srcdoc' }, options)), {}))
10
10
  .addNode(YfmHtmlBlockConsts.NodeName, () => ({
11
11
  fromMd: {
12
12
  tokenSpec: {
13
13
  name: YfmHtmlBlockConsts.NodeName,
14
14
  type: 'node',
15
15
  noCloseToken: true,
16
- getAttrs: (token) => { var _a; return Object.fromEntries((_a = token.attrs) !== null && _a !== void 0 ? _a : []); },
16
+ getAttrs: ({ content }) => ({ srcdoc: content }),
17
17
  },
18
18
  },
19
19
  spec: {
@@ -2,7 +2,7 @@ import { PluginOptions } from '@diplodoc/html-extension/plugin/transform';
2
2
  import type { IHTMLIFrameElementConfig } from '@diplodoc/html-extension/runtime';
3
3
  import { Action, ExtensionAuto } from '../../../core';
4
4
  import { YfmHtmlBlockAction } from './YfmHtmlBlockSpecs/const';
5
- export interface YfmHtmlBlockOptions extends Omit<PluginOptions, 'runtimeJsPath' | 'containerClasses' | 'bundle'> {
5
+ export interface YfmHtmlBlockOptions extends Omit<PluginOptions, 'runtimeJsPath' | 'containerClasses' | 'bundle' | 'embeddingMode'> {
6
6
  useConfig?: () => IHTMLIFrameElementConfig | undefined;
7
7
  }
8
8
  export declare const YfmHtmlBlock: ExtensionAuto<YfmHtmlBlockOptions>;
@@ -5,5 +5,14 @@
5
5
  "row.add.before": "Add row before",
6
6
  "row.add.after": "Add row after",
7
7
  "row.remove": "Remove row",
8
- "table.remove": "Remove table"
8
+ "table.remove": "Remove table",
9
+ "table.menu.cell.align.left": "Align cell content to the left",
10
+ "table.menu.cell.align.right": "Align cell content to the right",
11
+ "table.menu.cell.align.center": "Align cell content to the center",
12
+ "table.menu.row.add": "Add row after",
13
+ "table.menu.row.remove": "Remove row",
14
+ "table.menu.column.add": "Add column after",
15
+ "table.menu.column.remove": "Remove column",
16
+ "table.menu.convert.yfm": "Convert to YFM table",
17
+ "table.menu.table.remove": "Remove table"
9
18
  }
@@ -1,4 +1,4 @@
1
- export declare const i18n: <G extends "column.add.before" | "column.add.after" | "column.remove" | "row.add.before" | "row.add.after" | "row.remove" | "table.remove", S extends string>(key: G | (string extends S ? S : never), params?: {
1
+ export declare const i18n: <G extends "column.add.before" | "column.add.after" | "column.remove" | "row.add.before" | "row.add.after" | "row.remove" | "table.remove" | "table.menu.cell.align.left" | "table.menu.cell.align.right" | "table.menu.cell.align.center" | "table.menu.row.add" | "table.menu.row.remove" | "table.menu.column.add" | "table.menu.column.remove" | "table.menu.convert.yfm" | "table.menu.table.remove", S extends string>(key: G | (string extends S ? S : never), params?: {
2
2
  [key: string]: any;
3
3
  } | undefined) => S extends G ? {
4
4
  "column.add.before": string;
@@ -8,4 +8,13 @@ export declare const i18n: <G extends "column.add.before" | "column.add.after" |
8
8
  "row.add.after": string;
9
9
  "row.remove": string;
10
10
  "table.remove": string;
11
+ "table.menu.cell.align.left": string;
12
+ "table.menu.cell.align.right": string;
13
+ "table.menu.cell.align.center": string;
14
+ "table.menu.row.add": string;
15
+ "table.menu.row.remove": string;
16
+ "table.menu.column.add": string;
17
+ "table.menu.column.remove": string;
18
+ "table.menu.convert.yfm": string;
19
+ "table.menu.table.remove": string;
11
20
  }[G] : string;
@@ -5,5 +5,14 @@
5
5
  "row.add.before": "Добавить строку до",
6
6
  "row.add.after": "Добавить строку после",
7
7
  "row.remove": "Удалить строку",
8
- "table.remove": "Удалить таблицу"
8
+ "table.remove": "Удалить таблицу",
9
+ "table.menu.cell.align.left": "Выровнять контент ячейки по левому краю",
10
+ "table.menu.cell.align.right": "Выровнять контент ячейки по правому краю",
11
+ "table.menu.cell.align.center": "Выровнять контент ячейки по центру",
12
+ "table.menu.row.add": "Добавить строку после",
13
+ "table.menu.row.remove": "Удалить строку",
14
+ "table.menu.column.add": "Добавить столбец после",
15
+ "table.menu.column.remove": "Удалить столбец",
16
+ "table.menu.convert.yfm": "Преобразовать в таблицу YFM",
17
+ "table.menu.table.remove": "Удалить таблицу"
9
18
  }
@@ -1,6 +1,7 @@
1
1
  import { RefObject } from 'react';
2
2
  import { EditorView } from 'prosemirror-view';
3
- export declare const useNodeEditing: ({ nodeRef, view, }: {
3
+ export interface UseNodeEditingArgs {
4
4
  nodeRef: RefObject<HTMLElement>;
5
5
  view: EditorView;
6
- }) => [boolean, () => void, () => void, () => void];
6
+ }
7
+ export declare const useNodeEditing: ({ nodeRef, view }: UseNodeEditingArgs) => [boolean, () => void, () => void, () => void];
@@ -1,6 +1,6 @@
1
1
  import { useEffect } from 'react';
2
2
  import { useBooleanState } from './hooks';
3
- export const useNodeEditing = ({ nodeRef, view, }) => {
3
+ export const useNodeEditing = ({ nodeRef, view }) => {
4
4
  const state = useBooleanState(false);
5
5
  const [, , unsetEdit, toggleEdit] = state;
6
6
  useEffect(() => {
@@ -0,0 +1,22 @@
1
+ import React, { RefObject } from 'react';
2
+ export declare type ResizeDirection = 'left' | 'right';
3
+ export interface UseNodeResizingArgs {
4
+ width?: number;
5
+ height?: number;
6
+ onResize?: ({ width, height }: {
7
+ width?: number;
8
+ height?: number;
9
+ }) => void;
10
+ ref: RefObject<HTMLImageElement | HTMLDivElement> | null;
11
+ delay?: number;
12
+ threshold?: number;
13
+ minWidth?: number;
14
+ }
15
+ export declare const useNodeResizing: ({ width, height, onResize, ref, delay, threshold, minWidth, }: UseNodeResizingArgs) => {
16
+ startResizing: (event: React.MouseEvent<HTMLElement>, direction: ResizeDirection) => void;
17
+ state: {
18
+ resizing: boolean;
19
+ width: number | undefined;
20
+ height: number | undefined;
21
+ };
22
+ };
@@ -0,0 +1,77 @@
1
+ import { useEffect, useState } from 'react';
2
+ import throttle from 'lodash/throttle';
3
+ import { useBooleanState } from './hooks';
4
+ const RESIZE_DELAY = 50;
5
+ const MIN_WIDTH = 40;
6
+ const THRESHOLD = 4;
7
+ export const useNodeResizing = ({ width, height, onResize, ref, delay = RESIZE_DELAY, threshold = THRESHOLD, minWidth = MIN_WIDTH, }) => {
8
+ const state = useBooleanState(false);
9
+ const [resizing, , , toggleResizing] = state;
10
+ const [initialWidth, setInitialWidth] = useState(width);
11
+ const [initialHeight, setInitialHeight] = useState(height);
12
+ const [currentWidth, setCurrentWidth] = useState(width);
13
+ const [currentHeight, setCurrentHeight] = useState(height);
14
+ // The dimensions specified as arguments take primacy over
15
+ // the dimensions detected during the mouse movement.
16
+ useEffect(() => {
17
+ if (width !== initialWidth) {
18
+ setCurrentWidth(width);
19
+ setInitialWidth(width);
20
+ }
21
+ if (height !== initialHeight) {
22
+ setCurrentHeight(height);
23
+ setInitialHeight(height);
24
+ }
25
+ }, [width, height, initialWidth, initialHeight]);
26
+ const startResizing = (event, direction) => {
27
+ // prohibit the selection of text and other artifacts when resizing.
28
+ event.preventDefault();
29
+ const element = ref === null || ref === void 0 ? void 0 : ref.current;
30
+ if (!element) {
31
+ throw new Error('Reference element not found!');
32
+ }
33
+ const startX = event.pageX;
34
+ const startWidth = element.getBoundingClientRect().width || 0;
35
+ const startHeight = element.getBoundingClientRect().height || 0;
36
+ let animationFrameId;
37
+ const handleMouseMove = throttle((event) => {
38
+ if (animationFrameId) {
39
+ cancelAnimationFrame(animationFrameId);
40
+ }
41
+ animationFrameId = requestAnimationFrame(() => {
42
+ const currentX = event.pageX;
43
+ const diffX = currentX - startX;
44
+ const newWidthByDirection = direction === 'right' ? startWidth + diffX : startWidth - diffX;
45
+ if (Math.abs(newWidthByDirection - startWidth) >= threshold) {
46
+ const newWidth = newWidthByDirection >= minWidth ? newWidthByDirection : minWidth;
47
+ const newHeight = (startHeight / startWidth) * newWidth;
48
+ setCurrentWidth(newWidth);
49
+ setCurrentHeight(newHeight);
50
+ onResize === null || onResize === void 0 ? void 0 : onResize({
51
+ width: !initialWidth && initialWidth !== 0 ? undefined : newWidth,
52
+ height: !initialHeight && initialHeight !== 0 ? undefined : newHeight,
53
+ });
54
+ }
55
+ });
56
+ }, delay);
57
+ const handleMouseUp = () => {
58
+ document.removeEventListener('mousemove', handleMouseMove);
59
+ document.removeEventListener('mouseup', handleMouseUp);
60
+ toggleResizing();
61
+ if (animationFrameId) {
62
+ cancelAnimationFrame(animationFrameId);
63
+ }
64
+ };
65
+ document.addEventListener('mousemove', handleMouseMove);
66
+ document.addEventListener('mouseup', handleMouseUp);
67
+ toggleResizing();
68
+ };
69
+ return {
70
+ startResizing,
71
+ state: {
72
+ resizing,
73
+ width: !initialWidth && initialWidth !== 0 ? undefined : currentWidth,
74
+ height: !initialHeight && initialHeight !== 0 ? undefined : currentHeight,
75
+ },
76
+ };
77
+ };
@@ -1,2 +1,2 @@
1
1
  /** During build process, the current version will be injected here */
2
- export const VERSION = typeof '13.16.0' !== 'undefined' ? '13.16.0' : 'unknown';
2
+ export const VERSION = typeof '13.17.1' !== 'undefined' ? '13.17.1' : 'unknown';