@gravity-ui/markdown-editor 13.17.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 (38) 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/i18n/yfm-table/en.json +10 -1
  12. package/build/cjs/i18n/yfm-table/index.d.ts +10 -1
  13. package/build/cjs/i18n/yfm-table/ru.json +10 -1
  14. package/build/cjs/react-utils/useNodeEditing.d.ts +3 -2
  15. package/build/cjs/react-utils/useNodeEditing.js +1 -1
  16. package/build/cjs/react-utils/useNodeResizing.d.ts +22 -0
  17. package/build/cjs/react-utils/useNodeResizing.js +82 -0
  18. package/build/cjs/version.js +1 -1
  19. package/build/esm/extensions/behavior/Resizable/Resizable.css +38 -0
  20. package/build/esm/extensions/behavior/Resizable/Resizable.d.ts +10 -0
  21. package/build/esm/extensions/behavior/Resizable/Resizable.js +10 -0
  22. package/build/esm/extensions/markdown/Table/plugins/TableCellContextPlugin/index.js +19 -9
  23. package/build/esm/extensions/yfm/ImgSize/plugins/ImgSizeNodeView/ImgSettingsButton.css +6 -0
  24. package/build/esm/extensions/yfm/ImgSize/plugins/ImgSizeNodeView/ImgSettingsButton.d.ts +7 -1
  25. package/build/esm/extensions/yfm/ImgSize/plugins/ImgSizeNodeView/ImgSettingsButton.js +23 -38
  26. package/build/esm/extensions/yfm/ImgSize/plugins/ImgSizeNodeView/NodeView.d.ts +1 -1
  27. package/build/esm/extensions/yfm/ImgSize/plugins/ImgSizeNodeView/NodeView.js +63 -5
  28. package/build/esm/extensions/yfm/Mermaid/MermaidNodeView/MermaidView.js +5 -4
  29. package/build/esm/i18n/yfm-table/en.json +10 -1
  30. package/build/esm/i18n/yfm-table/index.d.ts +10 -1
  31. package/build/esm/i18n/yfm-table/ru.json +10 -1
  32. package/build/esm/react-utils/useNodeEditing.d.ts +3 -2
  33. package/build/esm/react-utils/useNodeEditing.js +1 -1
  34. package/build/esm/react-utils/useNodeResizing.d.ts +22 -0
  35. package/build/esm/react-utils/useNodeResizing.js +77 -0
  36. package/build/esm/version.js +1 -1
  37. package/build/styles.css +44 -0
  38. package/package.json +2 -2
@@ -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,9 @@
1
+ import React from 'react';
2
+ export interface ResizableProps {
3
+ children: React.ReactNode;
4
+ onResizeLeft: (event: React.MouseEvent<HTMLElement>) => void;
5
+ onResizeRight: (event: React.MouseEvent<HTMLElement>) => void;
6
+ hover?: boolean;
7
+ resizing?: boolean;
8
+ }
9
+ export declare const Resizable: React.FC<ResizableProps>;
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Resizable = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const react_1 = tslib_1.__importDefault(require("react"));
6
+ const classname_1 = require("../../../classname");
7
+ const b = (0, classname_1.cn)('resizable');
8
+ const Resizer = ({ onMouseDown, direction }) => (react_1.default.createElement("div", { className: b('resizer-wrapper', { [direction]: true }), role: "button", tabIndex: 0, onMouseDown: onMouseDown },
9
+ react_1.default.createElement("div", { className: b('resizer') })));
10
+ const Resizable = ({ hover, resizing, children, onResizeLeft, onResizeRight, }) => (react_1.default.createElement("div", { className: b({ hover, resizing }) },
11
+ children,
12
+ react_1.default.createElement(Resizer, { onMouseDown: onResizeLeft, direction: "left" }),
13
+ react_1.default.createElement(Resizer, { onMouseDown: onResizeRight, direction: "right" })));
14
+ exports.Resizable = Resizable;
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.tableCellContextPlugin = void 0;
4
4
  const prosemirror_state_1 = require("prosemirror-state");
5
5
  const core_1 = require("../../../../../core");
6
+ const yfm_table_1 = require("../../../../../i18n/yfm-table");
6
7
  const yfm_1 = require("../../../../yfm");
7
8
  const TableSpecs_1 = require("../../TableSpecs");
8
9
  const innerActions_1 = require("../../actions/innerActions");
@@ -17,18 +18,27 @@ const tableCellContextPlugin = () => new prosemirror_state_1.Plugin({
17
18
  } }))(view);
18
19
  return new view_1.TableCellContextView(view, [
19
20
  [
20
- { action: actions.setCellLeftAlign, text: 'left' },
21
- { action: actions.setCellCenterAlign, text: 'center' },
22
- { action: actions.setCellRightAlign, text: 'right' },
21
+ {
22
+ action: actions.setCellLeftAlign,
23
+ text: (0, yfm_table_1.i18n)('table.menu.cell.align.left'),
24
+ },
25
+ {
26
+ action: actions.setCellCenterAlign,
27
+ text: (0, yfm_table_1.i18n)('table.menu.cell.align.center'),
28
+ },
29
+ {
30
+ action: actions.setCellRightAlign,
31
+ text: (0, yfm_table_1.i18n)('table.menu.cell.align.right'),
32
+ },
23
33
  ],
24
34
  [
25
- { action: actions.addRow, text: 'add row' },
26
- { action: actions.deleteRow, text: 'del row' },
27
- { action: actions.addColumn, text: 'add column' },
28
- { action: actions.deleteColumn, text: 'del column' },
35
+ { action: actions.addRow, text: (0, yfm_table_1.i18n)('table.menu.row.add') },
36
+ { action: actions.deleteRow, text: (0, yfm_table_1.i18n)('table.menu.row.remove') },
37
+ { action: actions.addColumn, text: (0, yfm_table_1.i18n)('table.menu.column.add') },
38
+ { action: actions.deleteColumn, text: (0, yfm_table_1.i18n)('table.menu.column.remove') },
29
39
  ],
30
- { action: actions.convert, text: 'convert to yfm table' },
31
- { action: actions.deleteTable, text: 'del table' },
40
+ { action: actions.convert, text: (0, yfm_table_1.i18n)('table.menu.convert.yfm') },
41
+ { action: actions.deleteTable, text: (0, yfm_table_1.i18n)('table.menu.table.remove') },
32
42
  ], [schema.nodes[TableSpecs_1.TableNode.HeaderCell], schema.nodes[TableSpecs_1.TableNode.DataCell]]);
33
43
  },
34
44
  });
@@ -0,0 +1,6 @@
1
+ .g-md-img-settings-button {
2
+ position: absolute;
3
+ z-index: 2;
4
+ top: 3px;
5
+ right: 3px;
6
+ }
@@ -5,6 +5,11 @@ export declare const ImgSettingsButton: React.FC<{
5
5
  node: Node;
6
6
  view: EditorView;
7
7
  getPos: () => number | undefined;
8
- nodeRef: RefObject<HTMLElement>;
9
8
  updateAttributes: (o: object) => void;
9
+ nodeRef: RefObject<HTMLDivElement>;
10
+ visible: boolean;
11
+ toggleEdit: () => void;
12
+ edit: boolean;
13
+ unsetEdit: () => void;
14
+ onDelete: () => void;
10
15
  }>;
@@ -5,49 +5,33 @@ const tslib_1 = require("tslib");
5
5
  const react_1 = tslib_1.__importStar(require("react"));
6
6
  const icons_1 = require("@gravity-ui/icons");
7
7
  const uikit_1 = require("@gravity-ui/uikit");
8
+ const classname_1 = require("../../../../../classname");
8
9
  const common_1 = require("../../../../../i18n/common");
9
10
  const hooks_1 = require("../../../../../react-utils/hooks");
10
- const useNodeEditing_1 = require("../../../../../react-utils/useNodeEditing");
11
- const useNodeHovered_1 = require("../../../../../react-utils/useNodeHovered");
12
- const remove_node_1 = require("../../../../../utils/remove-node");
13
- const const_1 = require("../../const");
14
11
  const ImageForm_1 = require("./ImageForm");
15
- const ImgSettingsButton = function ({ node, view, getPos, nodeRef, updateAttributes }) {
12
+ const b = (0, classname_1.cn)('img-settings-button');
13
+ const ImgSettingsButton = function ({ node, view, updateAttributes, visible, edit, toggleEdit, nodeRef, unsetEdit, onDelete, }) {
16
14
  const [popupOpen, setPopupOpen, unsetPopupOpen] = (0, hooks_1.useBooleanState)(false);
17
15
  const placement = ['bottom-end', 'bottom-start'];
18
16
  const buttonRef = (0, react_1.useRef)(null);
19
- const isNodeHovered = (0, useNodeHovered_1.useNodeHovered)(nodeRef);
20
- const isButtonHovered = (0, useNodeHovered_1.useNodeHovered)(buttonRef);
21
- const [edit, setEditing, unsetEdit, toggleEdit] = (0, useNodeEditing_1.useNodeEditing)({ nodeRef, view });
22
- const visible = (isNodeHovered || isButtonHovered || popupOpen) && !edit;
23
- (0, react_1.useEffect)(() => {
24
- var _a;
25
- if ((_a = const_1.imageRendererKey.getState(view.state)) === null || _a === void 0 ? void 0 : _a.linkAdded) {
26
- setEditing();
27
- }
28
- }, [view, setEditing]);
29
- if (edit)
30
- return (react_1.default.createElement(ImageForm_1.ImageForm, { node: node, view: view, updateAttributes: updateAttributes, dom: nodeRef, unsetEdit: unsetEdit }));
31
- return visible ? (react_1.default.createElement(react_1.default.Fragment, null,
32
- react_1.default.createElement(uikit_1.Button, { onClick: setPopupOpen, ref: buttonRef, size: "s", view: 'raised', style: { position: 'absolute', right: '3px', top: '3px' } },
33
- react_1.default.createElement(uikit_1.Icon, { data: icons_1.Ellipsis })),
34
- react_1.default.createElement(uikit_1.Popup, { open: popupOpen, anchorRef: buttonRef, onClose: unsetPopupOpen, placement: placement },
17
+ const handleEdit = () => {
18
+ toggleEdit();
19
+ unsetPopupOpen();
20
+ };
21
+ const isVisibleImageForm = edit;
22
+ const isVisibleEditButton = !edit && (visible || popupOpen);
23
+ const isVisiblePopup = !edit && popupOpen;
24
+ const handleEditButtonClick = (event) => {
25
+ event.preventDefault();
26
+ setPopupOpen();
27
+ };
28
+ return (react_1.default.createElement(react_1.default.Fragment, null,
29
+ isVisibleImageForm && (react_1.default.createElement(ImageForm_1.ImageForm, { node: node, view: view, updateAttributes: updateAttributes, dom: nodeRef, unsetEdit: unsetEdit })),
30
+ isVisibleEditButton && (react_1.default.createElement(uikit_1.Button, { onClick: handleEditButtonClick, ref: buttonRef, size: "s", view: 'raised', className: b() },
31
+ react_1.default.createElement(uikit_1.Icon, { data: icons_1.Ellipsis }))),
32
+ react_1.default.createElement(uikit_1.Popup, { open: isVisiblePopup, anchorRef: buttonRef, onClose: unsetPopupOpen, placement: placement },
35
33
  react_1.default.createElement(uikit_1.Menu, null,
36
- react_1.default.createElement(uikit_1.Menu.Item, { onClick: () => {
37
- toggleEdit();
38
- unsetPopupOpen();
39
- } }, (0, common_1.i18n)('edit')),
40
- react_1.default.createElement(uikit_1.Menu.Item, { onClick: () => {
41
- const pos = getPos();
42
- if (pos === undefined)
43
- return;
44
- (0, remove_node_1.removeNode)({
45
- node,
46
- pos,
47
- tr: view.state.tr,
48
- dispatch: view.dispatch,
49
- });
50
- view.focus();
51
- } }, (0, common_1.i18n)('delete')))))) : null;
34
+ react_1.default.createElement(uikit_1.Menu.Item, { onClick: handleEdit }, (0, common_1.i18n)('edit')),
35
+ react_1.default.createElement(uikit_1.Menu.Item, { onClick: onDelete }, (0, common_1.i18n)('delete'))))));
52
36
  };
53
37
  exports.ImgSettingsButton = ImgSettingsButton;
@@ -1,4 +1,4 @@
1
1
  import React from 'react';
2
- import { ReactNodeViewProps } from '../../../../../react-utils/react-node-view';
2
+ import { ReactNodeViewProps } from '../../../../../react-utils';
3
3
  export declare const cnImgSizeNodeView: import("@bem-react/classname").ClassNameFormatter;
4
4
  export declare const ImageNodeView: React.FC<ReactNodeViewProps>;
@@ -4,12 +4,70 @@ exports.ImageNodeView = exports.cnImgSizeNodeView = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  const react_1 = tslib_1.__importStar(require("react"));
6
6
  const classname_1 = require("../../../../../classname");
7
+ const react_utils_1 = require("../../../../../react-utils");
8
+ const useNodeResizing_1 = require("../../../../../react-utils/useNodeResizing");
9
+ const utils_1 = require("../../../../../utils");
10
+ const Resizable_1 = require("../../../../behavior/Resizable/Resizable");
11
+ const ImgSizeSpecs_1 = require("../../ImgSizeSpecs");
12
+ const const_1 = require("../../const");
7
13
  const ImgSettingsButton_1 = require("./ImgSettingsButton");
8
14
  exports.cnImgSizeNodeView = (0, classname_1.cn)('img-size-node-view');
9
15
  const ImageNodeView = ({ node, view, getPos, updateAttributes, }) => {
10
- const ref = (0, react_1.useRef)(null);
11
- return (react_1.default.createElement(react_1.default.Fragment, null,
12
- react_1.default.createElement(ImgSettingsButton_1.ImgSettingsButton, { node: node, view: view, getPos: getPos, updateAttributes: updateAttributes, nodeRef: ref }),
13
- react_1.default.createElement("img", Object.assign({}, node.attrs, { ref: ref }))));
16
+ const imageContainerRef = (0, react_1.useRef)(null);
17
+ const imageRef = (0, react_1.useRef)(null);
18
+ const alt = node.attrs[ImgSizeSpecs_1.ImgSizeAttr.Alt] || '';
19
+ const initialHeight = node.attrs[ImgSizeSpecs_1.ImgSizeAttr.Height];
20
+ const initialWidth = node.attrs[ImgSizeSpecs_1.ImgSizeAttr.Width];
21
+ const src = node.attrs[ImgSizeSpecs_1.ImgSizeAttr.Src] || '';
22
+ const title = node.attrs[ImgSizeSpecs_1.ImgSizeAttr.Title] || '';
23
+ const isNodeHovered = (0, react_utils_1.useNodeHovered)(imageContainerRef);
24
+ const [edit, setEditing, unsetEdit, toggleEdit] = (0, react_utils_1.useNodeEditing)({
25
+ nodeRef: imageContainerRef,
26
+ view,
27
+ });
28
+ const handleResize = (0, react_1.useCallback)(({ width, height }) => {
29
+ updateAttributes({
30
+ width: width === undefined ? undefined : String(Math.round(width)),
31
+ height: height === undefined ? undefined : String(Math.round(height)),
32
+ name: title,
33
+ alt,
34
+ });
35
+ }, [alt, title, updateAttributes]);
36
+ const { state, startResizing } = (0, useNodeResizing_1.useNodeResizing)({
37
+ width: initialWidth,
38
+ height: initialHeight,
39
+ ref: imageRef,
40
+ onResize: handleResize,
41
+ });
42
+ const style = {
43
+ width: state.width ? `${state.width}px` : '',
44
+ height: state.height ? `${state.height}px` : '',
45
+ transition: 'width 0.15s ease-out, height 0.15s ease-out',
46
+ };
47
+ const handleDelete = (0, react_1.useCallback)(() => {
48
+ const pos = getPos();
49
+ if (pos === undefined)
50
+ return;
51
+ (0, utils_1.removeNode)({
52
+ node,
53
+ pos,
54
+ tr: view.state.tr,
55
+ dispatch: view.dispatch,
56
+ });
57
+ view.focus();
58
+ }, [getPos, node, view]);
59
+ const createHandleResize = (direction) => (event) => {
60
+ startResizing(event, direction);
61
+ };
62
+ (0, react_1.useEffect)(() => {
63
+ var _a;
64
+ if ((_a = const_1.imageRendererKey.getState(view.state)) === null || _a === void 0 ? void 0 : _a.linkAdded) {
65
+ setEditing();
66
+ }
67
+ }, [view, setEditing]);
68
+ return (react_1.default.createElement("div", { ref: imageContainerRef },
69
+ react_1.default.createElement(Resizable_1.Resizable, { hover: isNodeHovered, resizing: state.resizing, onResizeLeft: createHandleResize('left'), onResizeRight: createHandleResize('right') },
70
+ react_1.default.createElement(ImgSettingsButton_1.ImgSettingsButton, { node: node, view: view, getPos: getPos, updateAttributes: updateAttributes, visible: isNodeHovered && !edit && !state.resizing, edit: edit, toggleEdit: toggleEdit, nodeRef: imageRef, onDelete: handleDelete, unsetEdit: unsetEdit }),
71
+ react_1.default.createElement("img", { ref: imageRef, src: src, alt: alt, style: style }))));
14
72
  };
15
73
  exports.ImageNodeView = ImageNodeView;
@@ -7,8 +7,9 @@ const icons_1 = require("@gravity-ui/icons");
7
7
  const uikit_1 = require("@gravity-ui/uikit");
8
8
  const classname_1 = require("../../../../classname");
9
9
  const TextInput_1 = require("../../../../forms/TextInput");
10
- const hooks_1 = require("../../../../react-utils/hooks");
11
- const remove_node_1 = require("../../../../utils/remove-node");
10
+ const common_1 = require("../../../../i18n/common");
11
+ const react_utils_1 = require("../../../../react-utils");
12
+ const utils_1 = require("../../../../utils");
12
13
  const const_1 = require("../MermaidSpecs/const");
13
14
  exports.cnMermaid = (0, classname_1.cn)('Mermaid');
14
15
  exports.cnDiagramHelper = (0, classname_1.cn)('MermaidHelper');
@@ -58,8 +59,8 @@ const DiagramEditMode = ({ initialText, onSave, onCancel, mermaidInstance }) =>
58
59
  };
59
60
  const MermaidView = ({ onChange, node, getPos, view, getMermaidInstance }) => {
60
61
  const [mermaidInstance, setMermaidInstance] = (0, react_1.useState)(null);
61
- const [editing, setEditing, unsetEditing, toggleEditing] = (0, hooks_1.useBooleanState)(Boolean(node.attrs[const_1.MermaidConsts.NodeAttrs.newCreated]));
62
- const [menuOpen, , , toggleMenuOpen] = (0, hooks_1.useBooleanState)(false);
62
+ const [editing, setEditing, unsetEditing, toggleEditing] = (0, react_utils_1.useBooleanState)(Boolean(node.attrs[const_1.MermaidConsts.NodeAttrs.newCreated]));
63
+ const [menuOpen, , , toggleMenuOpen] = (0, react_utils_1.useBooleanState)(false);
63
64
  const buttonRef = (0, react_1.useRef)(null);
64
65
  (0, react_1.useEffect)(() => {
65
66
  const waitForMermaid = () => setTimeout(() => {
@@ -88,17 +89,17 @@ const MermaidView = ({ onChange, node, getPos, view, getMermaidInstance }) => {
88
89
  react_1.default.createElement(uikit_1.Menu.Item, { onClick: () => {
89
90
  toggleEditing();
90
91
  toggleMenuOpen();
91
- } }, "Edit"),
92
+ } }, (0, common_1.i18n)('edit')),
92
93
  react_1.default.createElement(uikit_1.Menu.Item, { onClick: () => {
93
94
  const pos = getPos();
94
95
  if (pos === undefined)
95
96
  return;
96
- (0, remove_node_1.removeNode)({
97
+ (0, utils_1.removeNode)({
97
98
  node,
98
99
  pos,
99
100
  tr: view.state.tr,
100
101
  dispatch: view.dispatch,
101
102
  });
102
- } }, "Remove"))))));
103
+ } }, (0, common_1.i18n)('remove')))))));
103
104
  };
104
105
  exports.MermaidView = MermaidView;
@@ -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];
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.useNodeEditing = void 0;
4
4
  const react_1 = require("react");
5
5
  const hooks_1 = require("./hooks");
6
- const useNodeEditing = ({ nodeRef, view, }) => {
6
+ const useNodeEditing = ({ nodeRef, view }) => {
7
7
  const state = (0, hooks_1.useBooleanState)(false);
8
8
  const [, , unsetEdit, toggleEdit] = state;
9
9
  (0, react_1.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,82 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.useNodeResizing = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const react_1 = require("react");
6
+ const throttle_1 = tslib_1.__importDefault(require("lodash/throttle"));
7
+ const hooks_1 = require("./hooks");
8
+ const RESIZE_DELAY = 50;
9
+ const MIN_WIDTH = 40;
10
+ const THRESHOLD = 4;
11
+ const useNodeResizing = ({ width, height, onResize, ref, delay = RESIZE_DELAY, threshold = THRESHOLD, minWidth = MIN_WIDTH, }) => {
12
+ const state = (0, hooks_1.useBooleanState)(false);
13
+ const [resizing, , , toggleResizing] = state;
14
+ const [initialWidth, setInitialWidth] = (0, react_1.useState)(width);
15
+ const [initialHeight, setInitialHeight] = (0, react_1.useState)(height);
16
+ const [currentWidth, setCurrentWidth] = (0, react_1.useState)(width);
17
+ const [currentHeight, setCurrentHeight] = (0, react_1.useState)(height);
18
+ // The dimensions specified as arguments take primacy over
19
+ // the dimensions detected during the mouse movement.
20
+ (0, react_1.useEffect)(() => {
21
+ if (width !== initialWidth) {
22
+ setCurrentWidth(width);
23
+ setInitialWidth(width);
24
+ }
25
+ if (height !== initialHeight) {
26
+ setCurrentHeight(height);
27
+ setInitialHeight(height);
28
+ }
29
+ }, [width, height, initialWidth, initialHeight]);
30
+ const startResizing = (event, direction) => {
31
+ // prohibit the selection of text and other artifacts when resizing.
32
+ event.preventDefault();
33
+ const element = ref === null || ref === void 0 ? void 0 : ref.current;
34
+ if (!element) {
35
+ throw new Error('Reference element not found!');
36
+ }
37
+ const startX = event.pageX;
38
+ const startWidth = element.getBoundingClientRect().width || 0;
39
+ const startHeight = element.getBoundingClientRect().height || 0;
40
+ let animationFrameId;
41
+ const handleMouseMove = (0, throttle_1.default)((event) => {
42
+ if (animationFrameId) {
43
+ cancelAnimationFrame(animationFrameId);
44
+ }
45
+ animationFrameId = requestAnimationFrame(() => {
46
+ const currentX = event.pageX;
47
+ const diffX = currentX - startX;
48
+ const newWidthByDirection = direction === 'right' ? startWidth + diffX : startWidth - diffX;
49
+ if (Math.abs(newWidthByDirection - startWidth) >= threshold) {
50
+ const newWidth = newWidthByDirection >= minWidth ? newWidthByDirection : minWidth;
51
+ const newHeight = (startHeight / startWidth) * newWidth;
52
+ setCurrentWidth(newWidth);
53
+ setCurrentHeight(newHeight);
54
+ onResize === null || onResize === void 0 ? void 0 : onResize({
55
+ width: !initialWidth && initialWidth !== 0 ? undefined : newWidth,
56
+ height: !initialHeight && initialHeight !== 0 ? undefined : newHeight,
57
+ });
58
+ }
59
+ });
60
+ }, delay);
61
+ const handleMouseUp = () => {
62
+ document.removeEventListener('mousemove', handleMouseMove);
63
+ document.removeEventListener('mouseup', handleMouseUp);
64
+ toggleResizing();
65
+ if (animationFrameId) {
66
+ cancelAnimationFrame(animationFrameId);
67
+ }
68
+ };
69
+ document.addEventListener('mousemove', handleMouseMove);
70
+ document.addEventListener('mouseup', handleMouseUp);
71
+ toggleResizing();
72
+ };
73
+ return {
74
+ startResizing,
75
+ state: {
76
+ resizing,
77
+ width: !initialWidth && initialWidth !== 0 ? undefined : currentWidth,
78
+ height: !initialHeight && initialHeight !== 0 ? undefined : currentHeight,
79
+ },
80
+ };
81
+ };
82
+ exports.useNodeResizing = useNodeResizing;
@@ -2,4 +2,4 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.VERSION = void 0;
4
4
  /** During build process, the current version will be injected here */
5
- exports.VERSION = typeof '13.17.0' !== 'undefined' ? '13.17.0' : 'unknown';
5
+ exports.VERSION = typeof '13.17.1' !== 'undefined' ? '13.17.1' : 'unknown';
@@ -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
  };
@@ -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.17.0' !== 'undefined' ? '13.17.0' : 'unknown';
2
+ export const VERSION = typeof '13.17.1' !== 'undefined' ? '13.17.1' : 'unknown';
package/build/styles.css CHANGED
@@ -823,6 +823,44 @@ img.ProseMirror-separator {
823
823
  animation: placeholder_blink 1s;
824
824
  animation-iteration-count: infinite;
825
825
  }
826
+ body :has(.g-md-resizable_resizing) {
827
+ cursor: col-resize;
828
+ }
829
+
830
+ .g-md-resizable {
831
+ position: relative;
832
+ }
833
+ .g-md-resizable_resizing .g-md-resizable__resizer-wrapper, .g-md-resizable_hover .g-md-resizable__resizer-wrapper {
834
+ position: absolute;
835
+ z-index: 1;
836
+ top: 0;
837
+ display: flex;
838
+ justify-content: center;
839
+ align-items: center;
840
+ width: 20px;
841
+ height: 100%;
842
+ cursor: col-resize;
843
+ pointer-events: auto;
844
+ }
845
+ .g-md-resizable_resizing .g-md-resizable__resizer-wrapper_left, .g-md-resizable_hover .g-md-resizable__resizer-wrapper_left {
846
+ left: 0;
847
+ }
848
+ .g-md-resizable_resizing .g-md-resizable__resizer-wrapper_right, .g-md-resizable_hover .g-md-resizable__resizer-wrapper_right {
849
+ right: 0;
850
+ }
851
+ .g-md-resizable__resizer {
852
+ opacity: 0;
853
+ }
854
+ .g-md-resizable_resizing .g-md-resizable__resizer, .g-md-resizable_hover .g-md-resizable__resizer {
855
+ box-sizing: content-box;
856
+ width: 4px;
857
+ height: 50px;
858
+ max-height: 50%;
859
+ opacity: 1;
860
+ border-radius: 6px;
861
+ background: rgba(127, 127, 127, 0.8);
862
+ transition: opacity 300ms ease-in 0s;
863
+ }
826
864
  .g-md-editor.ProseMirror-focused .pm-node-selected {
827
865
  box-shadow: var(--g-color-text-info) 0 0 0 1px;
828
866
  }
@@ -1391,6 +1429,12 @@ img.ProseMirror-separator {
1391
1429
  position: relative;
1392
1430
  display: inline-block;
1393
1431
  }
1432
+ .g-md-img-settings-button {
1433
+ position: absolute;
1434
+ z-index: 2;
1435
+ top: 3px;
1436
+ right: 3px;
1437
+ }
1394
1438
  .yfm .yfm-note {
1395
1439
  max-width: 1296px;
1396
1440
  margin: 20px 0;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gravity-ui/markdown-editor",
3
- "version": "13.17.0",
3
+ "version": "13.17.1",
4
4
  "description": "Markdown wysiwyg and markup editor",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -278,7 +278,7 @@
278
278
  "@diplodoc/html-extension": "2.1.0",
279
279
  "@diplodoc/latex-extension": "^1.0.3",
280
280
  "@diplodoc/mermaid-extension": "^1.0.0",
281
- "@diplodoc/transform": "^4.5.0",
281
+ "@diplodoc/transform": ">=4.5.0 <4.19.0",
282
282
  "@gravity-ui/components": "^3.0.0",
283
283
  "@gravity-ui/uikit": "^6.11.0",
284
284
  "highlight.js": "^11.8.0",