@gravity-ui/markdown-editor 14.10.5 → 14.11.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.
@@ -3,24 +3,22 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.createYfmTabs = exports.createYfmTabsCommand = void 0;
4
4
  const utils_1 = require("@diplodoc/transform/lib/plugins/utils");
5
5
  const BaseSchema_1 = require("../../base/BaseSchema");
6
- const const_1 = require("./YfmTabsSpecs/const");
7
- const const_2 = require("./const");
8
- const _1 = require(".");
6
+ const const_1 = require("./const");
9
7
  const createYfmTabsCommand = (state, dispatch) => {
10
8
  if (dispatch) {
11
9
  const { schema } = state;
12
10
  const tabId = (0, utils_1.generateID)();
13
11
  const panelId = (0, utils_1.generateID)();
14
- const yfmTabsList = (0, _1.tabsListType)(schema);
15
- const yfmTabs = (0, _1.tabsType)(schema);
16
- const yfmTabPanel = (0, _1.tabPanelType)(state.schema).create({
12
+ const yfmTabsList = (0, const_1.tabsListType)(schema);
13
+ const yfmTabs = (0, const_1.tabsType)(schema);
14
+ const yfmTabPanel = (0, const_1.tabPanelType)(state.schema).create({
17
15
  [const_1.TabPanelAttrs.ariaLabelledby]: tabId,
18
16
  [const_1.TabPanelAttrs.id]: panelId,
19
- [const_1.TabPanelAttrs.class]: const_2.tabPanelActiveClassname,
17
+ [const_1.TabPanelAttrs.class]: const_1.tabPanelActiveClassname,
20
18
  }, (0, BaseSchema_1.pType)(state.schema).createAndFill());
21
- const yfmTab = (0, _1.tabType)(state.schema).create({
19
+ const yfmTab = (0, const_1.tabType)(state.schema).create({
22
20
  [const_1.TabAttrs.id]: tabId,
23
- [const_1.TabAttrs.class]: const_2.tabActiveClassname,
21
+ [const_1.TabAttrs.class]: const_1.tabActiveClassname,
24
22
  [const_1.TabAttrs.ariaControls]: panelId,
25
23
  [const_1.TabAttrs.dataDiplodocIsActive]: 'true',
26
24
  [const_1.TabAttrs.dataDiplodocid]: tabId,
@@ -1,5 +1,8 @@
1
- export { TabsNode, tabType, tabPanelType, tabsListType, tabsType } from './YfmTabsSpecs';
2
- export declare const tabActiveClassname = "yfm-tab active";
1
+ export * from './YfmTabsSpecs/const';
2
+ export { tabType, tabPanelType, tabsListType, tabsType } from './YfmTabsSpecs';
3
+ export declare const YFM_TAB_CLASSNAME = "yfm-tab";
4
+ export declare const DIPLODOC_ID_ATTR = "data-diplodoc-id";
5
+ export declare const tabActiveClassname: string;
3
6
  export declare const tabInactiveClassname = "yfm-tab";
4
7
  export declare const tabPanelActiveClassname = "yfm-tab-panel active";
5
8
  export declare const tabPanelInactiveClassname = "yfm-tab-panel";
@@ -1,13 +1,16 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.tabPanelInactiveClassname = exports.tabPanelActiveClassname = exports.tabInactiveClassname = exports.tabActiveClassname = exports.tabsType = exports.tabsListType = exports.tabPanelType = exports.tabType = exports.TabsNode = void 0;
3
+ exports.tabPanelInactiveClassname = exports.tabPanelActiveClassname = exports.tabInactiveClassname = exports.tabActiveClassname = exports.DIPLODOC_ID_ATTR = exports.YFM_TAB_CLASSNAME = exports.tabsType = exports.tabsListType = exports.tabPanelType = exports.tabType = void 0;
4
+ const tslib_1 = require("tslib");
5
+ tslib_1.__exportStar(require("./YfmTabsSpecs/const"), exports);
4
6
  var YfmTabsSpecs_1 = require("./YfmTabsSpecs");
5
- Object.defineProperty(exports, "TabsNode", { enumerable: true, get: function () { return YfmTabsSpecs_1.TabsNode; } });
6
7
  Object.defineProperty(exports, "tabType", { enumerable: true, get: function () { return YfmTabsSpecs_1.tabType; } });
7
8
  Object.defineProperty(exports, "tabPanelType", { enumerable: true, get: function () { return YfmTabsSpecs_1.tabPanelType; } });
8
9
  Object.defineProperty(exports, "tabsListType", { enumerable: true, get: function () { return YfmTabsSpecs_1.tabsListType; } });
9
10
  Object.defineProperty(exports, "tabsType", { enumerable: true, get: function () { return YfmTabsSpecs_1.tabsType; } });
10
- exports.tabActiveClassname = 'yfm-tab active';
11
- exports.tabInactiveClassname = 'yfm-tab';
11
+ exports.YFM_TAB_CLASSNAME = 'yfm-tab';
12
+ exports.DIPLODOC_ID_ATTR = 'data-diplodoc-id';
13
+ exports.tabActiveClassname = `${exports.YFM_TAB_CLASSNAME} active`;
14
+ exports.tabInactiveClassname = exports.YFM_TAB_CLASSNAME;
12
15
  exports.tabPanelActiveClassname = 'yfm-tab-panel active';
13
16
  exports.tabPanelInactiveClassname = 'yfm-tab-panel';
@@ -1,5 +1,5 @@
1
- import { Command, Plugin } from 'prosemirror-state';
2
- import { NodeWithPos } from 'prosemirror-utils';
1
+ import { type Command, Plugin } from 'prosemirror-state';
2
+ import { type NodeWithPos } from 'prosemirror-utils';
3
3
  export declare const dragAutoSwitch: () => Plugin<any>;
4
4
  export declare const tabPanelArrowDown: Command;
5
5
  export declare const tabEnter: Command;
@@ -39,12 +39,12 @@ class TabsAutoSwitchOnDragOver {
39
39
  const pos = view.posAtCoords({ left: event.clientX, top: event.clientY });
40
40
  if (pos) {
41
41
  const elem = (0, prosemirror_utils_1.findDomRefAtPos)(pos.pos, view.domAtPos.bind(view));
42
- const cutElem = elem.closest(TabsAutoSwitchOnDragOver.TAB_SELECTOR);
43
- if (cutElem === this._tabElem)
42
+ const tabElem = elem.closest(TabsAutoSwitchOnDragOver.TAB_SELECTOR);
43
+ if (tabElem === this._tabElem)
44
44
  return;
45
45
  this._clear();
46
- if (cutElem)
47
- this._setTabElem(cutElem);
46
+ if (tabElem)
47
+ this._setTabElem(tabElem);
48
48
  }
49
49
  }
50
50
  _clear() {
@@ -59,29 +59,7 @@ class TabsAutoSwitchOnDragOver {
59
59
  }
60
60
  _switchTab() {
61
61
  if (this._editorView.dragging && this._tabElem) {
62
- const pos = this._editorView.posAtDOM(this._tabElem, 0, -1);
63
- const $pos = this._editorView.state.doc.resolve(pos);
64
- const { state } = this._editorView;
65
- let { depth } = $pos;
66
- let tabId = '';
67
- let tabsNode = null;
68
- do {
69
- const node = $pos.node(depth);
70
- if (node.type === (0, const_2.tabType)(state.schema)) {
71
- tabId = node.attrs[const_1.TabAttrs.dataDiplodocid];
72
- continue;
73
- }
74
- if (node.type === (0, const_2.tabsType)(state.schema)) {
75
- tabsNode = { node, pos: $pos.before(depth) };
76
- break;
77
- }
78
- } while (--depth >= 0);
79
- if (tabId && tabsNode) {
80
- const { tr } = state;
81
- if (switchYfmTab(tabsNode, tabId, tr)) {
82
- this._editorView.dispatch(tr.setMeta('addToHistory', false));
83
- }
84
- }
62
+ (0, utils_3.switchTabByElem)(this._tabElem);
85
63
  }
86
64
  this._clear();
87
65
  }
@@ -91,49 +69,6 @@ TabsAutoSwitchOnDragOver.TAB_SELECTOR = '.yfm-tab:not([data-diplodoc-is-active=t
91
69
  TabsAutoSwitchOnDragOver.OPEN_TIMEOUT = 500; //ms
92
70
  TabsAutoSwitchOnDragOver.THROTTLE_WAIT = 50; //ms
93
71
  TabsAutoSwitchOnDragOver.view = (view) => new _a(view);
94
- function switchYfmTab({ node: tabsNode, pos: tabsPos }, tabId, tr) {
95
- const { schema } = tabsNode.type;
96
- if (tabsNode.type !== (0, const_2.tabsType)(schema))
97
- return false;
98
- const tabsList = tabsNode.firstChild;
99
- if ((tabsList === null || tabsList === void 0 ? void 0 : tabsList.type) !== (0, const_2.tabsListType)(schema))
100
- return false;
101
- const tabsListPos = tabsPos + 1;
102
- let panelId = null;
103
- tabsList.forEach((node, offset) => {
104
- if (node.type !== (0, const_2.tabType)(schema))
105
- return;
106
- const tabPos = tabsListPos + 1 + offset;
107
- const tabAttrs = Object.assign(Object.assign({}, node.attrs), { [const_1.TabAttrs.ariaSelected]: 'false', [const_1.TabAttrs.dataDiplodocIsActive]: 'false' });
108
- if (node.attrs[const_1.TabAttrs.dataDiplodocid] === tabId) {
109
- panelId = node.attrs[const_1.TabAttrs.ariaControls];
110
- tabAttrs[const_1.TabAttrs.ariaSelected] = 'true';
111
- tabAttrs[const_1.TabAttrs.dataDiplodocIsActive] = 'true';
112
- }
113
- tr.setNodeMarkup(tabPos, null, tabAttrs);
114
- });
115
- if (!panelId)
116
- return false;
117
- tabsNode.forEach((node, offset) => {
118
- var _b;
119
- if (node.type !== (0, const_2.tabPanelType)(schema))
120
- return;
121
- const tabPanelPos = tabsPos + 1 + offset;
122
- const tabPanelAttrs = Object.assign({}, node.attrs);
123
- const tabPanelClassList = new Set(((_b = node.attrs[const_1.TabPanelAttrs.class]) !== null && _b !== void 0 ? _b : '')
124
- .split(' ')
125
- .filter((val) => Boolean(val.trim())));
126
- if (node.attrs[const_1.TabPanelAttrs.id] === panelId) {
127
- tabPanelClassList.add('active');
128
- }
129
- else {
130
- tabPanelClassList.delete('active');
131
- }
132
- tabPanelAttrs[const_1.TabPanelAttrs.class] = Array.from(tabPanelClassList).join(' ');
133
- tr.setNodeMarkup(tabPanelPos, null, tabPanelAttrs);
134
- });
135
- return true;
136
- }
137
72
  const tabPanelArrowDown = (state, dispatch, view) => {
138
73
  var _b;
139
74
  const { selection: sel } = state;
@@ -189,22 +124,6 @@ const liftEmptyBlockFromTabPanel = (state, dispatch) => {
189
124
  return false;
190
125
  };
191
126
  exports.liftEmptyBlockFromTabPanel = liftEmptyBlockFromTabPanel;
192
- const makeTabsInactive = (tabNodes, tabPanels, tr) => {
193
- // Find all active tabs and make them inactive
194
- const activeTabs = tabNodes.filter((v) => v.node.attrs[const_1.TabAttrs.dataDiplodocIsActive] === 'true');
195
- if (activeTabs.length) {
196
- activeTabs.forEach((tab) => {
197
- tr.setNodeMarkup(tab.pos, null, Object.assign(Object.assign({}, tab.node.attrs), { class: const_2.tabInactiveClassname, [const_1.TabAttrs.dataDiplodocIsActive]: 'false' }));
198
- });
199
- }
200
- // Find all active panels and make them inactive
201
- const activePanels = tabPanels.filter((v) => v.node.attrs[const_1.TabPanelAttrs.class] === const_2.tabPanelActiveClassname);
202
- if (activePanels.length) {
203
- activePanels.forEach((tabPanel) => {
204
- tr.setNodeMarkup(tr.mapping.map(tabPanel.pos), null, Object.assign(Object.assign({}, tabPanel.node.attrs), { class: const_2.tabPanelInactiveClassname }));
205
- });
206
- }
207
- };
208
127
  const createTab = (afterTab, tabsParentNode) => (state, dispatch, view) => {
209
128
  const tabNodes = (0, prosemirror_utils_1.findChildren)(tabsParentNode.node, (node) => node.type.name === (0, const_2.tabType)(state.schema).name);
210
129
  const tabPanels = (0, prosemirror_utils_1.findChildren)(tabsParentNode.node, (tabNode) => {
@@ -217,14 +136,14 @@ const createTab = (afterTab, tabsParentNode) => (state, dispatch, view) => {
217
136
  const newPanel = (0, const_2.tabPanelType)(state.schema).create({
218
137
  [const_1.TabPanelAttrs.ariaLabelledby]: tabId,
219
138
  [const_1.TabPanelAttrs.id]: panelId,
220
- [const_1.TabPanelAttrs.class]: const_2.tabPanelActiveClassname,
139
+ [const_1.TabPanelAttrs.class]: const_2.tabPanelInactiveClassname,
221
140
  }, (0, __1.pType)(state.schema).createAndFill());
222
141
  const newTab = (0, const_2.tabType)(state.schema).create({
223
142
  [const_1.TabAttrs.id]: tabId,
224
143
  [const_1.TabAttrs.dataDiplodocid]: tabId,
225
144
  [const_1.TabAttrs.dataDiplodocKey]: tabId,
226
- [const_1.TabAttrs.dataDiplodocIsActive]: 'true',
227
- [const_1.TabAttrs.class]: const_2.tabActiveClassname,
145
+ [const_1.TabAttrs.dataDiplodocIsActive]: 'false',
146
+ [const_1.TabAttrs.class]: const_2.tabInactiveClassname,
228
147
  [const_1.TabAttrs.role]: 'tab',
229
148
  [const_1.TabAttrs.ariaControls]: panelId,
230
149
  });
@@ -236,12 +155,14 @@ const createTab = (afterTab, tabsParentNode) => (state, dispatch, view) => {
236
155
  tabPanels.forEach((v) => {
237
156
  v.pos = v.pos + tabsParentNode.pos + 1;
238
157
  });
239
- makeTabsInactive(tabNodes, tabPanels, tr);
240
158
  dispatch === null || dispatch === void 0 ? void 0 : dispatch(tr
241
159
  .insert(afterPanelNode.pos + afterPanelNode.node.nodeSize, newPanel)
242
160
  .insert(afterTab.pos + afterTab.node.nodeSize, newTab)
243
161
  .setSelection(prosemirror_state_1.TextSelection.create(tr.doc, afterTab.pos + afterTab.node.nodeSize + 1)));
244
162
  view === null || view === void 0 ? void 0 : view.focus();
163
+ if (view) {
164
+ (0, utils_3.execAfterPaint)(() => (0, utils_3.switchTabById)(view.dom, tabId));
165
+ }
245
166
  return true;
246
167
  };
247
168
  exports.createTab = createTab;
@@ -271,18 +192,16 @@ const removeTab = (tabToRemove, tabsParentNode) => (state, dispatch, view) => {
271
192
  v.pos = v.pos + tabsParentNode.pos + 1;
272
193
  });
273
194
  const newTabNode = tabNodes[newTabIdx];
274
- const newTabPanelNode = tabPanels[newTabIdx];
275
- makeTabsInactive(tabNodes, tabPanels, tr);
195
+ const newActiveTabId = newTabNode.node.attrs[const_1.TabAttrs.dataDiplodocid];
276
196
  tr
277
197
  // Delete panel
278
198
  .delete(panelToRemove.pos, panelToRemove.pos + panelToRemove.node.nodeSize)
279
199
  // Delete tab
280
200
  .delete(tabToRemove.pos, tabToRemove.pos + tabToRemove.node.nodeSize)
281
- // Set new active tab
282
- .setNodeMarkup(tr.mapping.map(newTabNode.pos), null, Object.assign(Object.assign({}, newTabNode.node.attrs), { class: const_2.tabActiveClassname, [const_1.TabAttrs.dataDiplodocIsActive]: 'true' }))
283
- // Set new active panel
284
- .setNodeMarkup(tr.mapping.map(newTabPanelNode.pos), null, Object.assign(Object.assign({}, newTabPanelNode.node.attrs), { class: const_2.tabPanelActiveClassname }))
285
201
  .setSelection(prosemirror_state_1.TextSelection.create(tr.doc, tr.mapping.map(newTabNode.pos + newTabNode.node.nodeSize - 1)));
202
+ // Set new active tab
203
+ if (view)
204
+ (0, utils_3.execAfterPaint)(() => (0, utils_3.switchTabById)(view.dom, newActiveTabId));
286
205
  }
287
206
  dispatch(tr);
288
207
  view === null || view === void 0 ? void 0 : view.focus();
@@ -1,2 +1,5 @@
1
- import { EditorView } from 'prosemirror-view';
1
+ import type { EditorView } from 'prosemirror-view';
2
+ export declare const execAfterPaint: (fn: () => void) => void;
2
3
  export declare const atEndOfPanel: (view?: EditorView) => number | false | null;
4
+ export declare const switchTabByElem: (tabElem: HTMLElement) => void;
5
+ export declare const switchTabById: (container: HTMLElement, tabId: string) => void;
@@ -1,7 +1,13 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.atEndOfPanel = void 0;
4
- const _1 = require(".");
3
+ exports.switchTabById = exports.switchTabByElem = exports.atEndOfPanel = exports.execAfterPaint = void 0;
4
+ const const_1 = require("./const");
5
+ const execAfterPaint = (fn) => {
6
+ requestAnimationFrame(() => {
7
+ requestAnimationFrame(fn);
8
+ });
9
+ };
10
+ exports.execAfterPaint = execAfterPaint;
5
11
  const atEndOfPanel = (view) => {
6
12
  if (!view)
7
13
  return null;
@@ -10,7 +16,7 @@ const atEndOfPanel = (view) => {
10
16
  const parent = $head.node(d), index = $head.indexAfter(d);
11
17
  if (index !== parent.childCount)
12
18
  return false;
13
- if (parent.type === (0, _1.tabPanelType)(view.state.schema)) {
19
+ if (parent.type === (0, const_1.tabPanelType)(view.state.schema)) {
14
20
  const panelPos = $head.before(d);
15
21
  return (view === null || view === void 0 ? void 0 : view.endOfTextblock('down')) ? panelPos : null;
16
22
  }
@@ -18,3 +24,17 @@ const atEndOfPanel = (view) => {
18
24
  return null;
19
25
  };
20
26
  exports.atEndOfPanel = atEndOfPanel;
27
+ const switchTabByElem = (tabElem) => {
28
+ if (tabElem.classList.contains(const_1.YFM_TAB_CLASSNAME)) {
29
+ tabElem.click();
30
+ }
31
+ };
32
+ exports.switchTabByElem = switchTabByElem;
33
+ const switchTabById = (container, tabId) => {
34
+ const selector = `.${const_1.YFM_TAB_CLASSNAME}[${const_1.DIPLODOC_ID_ATTR}="${tabId}"]`;
35
+ const tabElem = container.querySelector(selector);
36
+ if (tabElem) {
37
+ (0, exports.switchTabByElem)(tabElem);
38
+ }
39
+ };
40
+ exports.switchTabById = switchTabById;
@@ -1,4 +1,4 @@
1
- import { NodeViewConstructor } from 'prosemirror-view';
1
+ import { type NodeViewConstructor } from 'prosemirror-view';
2
2
  export declare const tabView: NodeViewConstructor;
3
3
  export declare const tabPanelView: NodeViewConstructor;
4
4
  export declare const vtabView: NodeViewConstructor;
@@ -1,25 +1,25 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.vtabInputView = exports.vtabView = exports.tabPanelView = exports.tabView = void 0;
4
+ const prosemirror_state_1 = require("prosemirror-state");
4
5
  const prosemirror_utils_1 = require("prosemirror-utils");
5
6
  const classname_1 = require("../../../classname");
7
+ const const_1 = require("./const");
6
8
  const icons_1 = require("./icons");
7
9
  const plugins_1 = require("./plugins");
8
- const _1 = require(".");
10
+ const utils_1 = require("./utils");
9
11
  const cnYfmTab = (0, classname_1.cn)('yfm-tab');
10
- const ignoreMutation = (node, view, getPos) => (mutation) => {
12
+ const ignoreMutation = (_node, _view, _getPos) => (mutation) => {
11
13
  if (mutation instanceof MutationRecord &&
12
14
  mutation.type === 'attributes' &&
13
15
  mutation.attributeName) {
14
- const newAttr = mutation.target.getAttribute(mutation.attributeName);
15
- view.dispatch(view.state.tr.setNodeMarkup(getPos(), null, Object.assign(Object.assign({}, node.attrs), { [mutation.attributeName]: newAttr })));
16
16
  return true;
17
17
  }
18
18
  return false;
19
19
  };
20
20
  const getTabNodes = (state, getPos) => {
21
- const currentTab = (0, prosemirror_utils_1.findParentNodeOfTypeClosestToPos)(state.tr.doc.resolve(getPos() + 1), (0, _1.tabType)(state.schema));
22
- const tabsParentNode = (0, prosemirror_utils_1.findParentNodeOfTypeClosestToPos)(state.tr.doc.resolve(getPos()), (0, _1.tabsType)(state.schema));
21
+ const currentTab = (0, prosemirror_utils_1.findParentNodeOfTypeClosestToPos)(state.tr.doc.resolve(getPos() + 1), (0, const_1.tabType)(state.schema));
22
+ const tabsParentNode = (0, prosemirror_utils_1.findParentNodeOfTypeClosestToPos)(state.tr.doc.resolve(getPos()), (0, const_1.tabsType)(state.schema));
23
23
  return { currentTab, tabsParentNode };
24
24
  };
25
25
  const tabView = (node, view, getPos) => {
@@ -31,6 +31,26 @@ const tabView = (node, view, getPos) => {
31
31
  wrapperElem.addEventListener('click', () => {
32
32
  // Click on parent node to trigger event listener that selects current tab
33
33
  tabElem.click();
34
+ {
35
+ /**
36
+ * Hack for empty tabs
37
+ *
38
+ * Problem: when clicking on an empty tab (without text content) it focuses, and selection doesn't move to beginning of tab
39
+ *
40
+ * Temporary fix: manually return focus to pm-view, move text selection to beginning of tab
41
+ */
42
+ view.focus();
43
+ // tab is empty
44
+ if (node.nodeSize < 3) {
45
+ (0, utils_1.execAfterPaint)(() => {
46
+ const pos = getPos();
47
+ if (pos !== undefined) {
48
+ const { tr } = view.state;
49
+ view.dispatch(tr.setSelection(prosemirror_state_1.TextSelection.create(tr.doc, pos + 1)).scrollIntoView());
50
+ }
51
+ });
52
+ }
53
+ }
34
54
  });
35
55
  const removeTabButton = document.createElement('div');
36
56
  removeTabButton.setAttribute('class', cnYfmTab('remove-button'));
@@ -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 '14.10.5' !== 'undefined' ? '14.10.5' : 'unknown';
5
+ exports.VERSION = typeof '14.11.0' !== 'undefined' ? '14.11.0' : 'unknown';
@@ -1,8 +1,6 @@
1
1
  import { generateID } from '@diplodoc/transform/lib/plugins/utils';
2
2
  import { pType } from '../../base/BaseSchema';
3
- import { TabAttrs, TabPanelAttrs, TabsAttrs } from './YfmTabsSpecs/const';
4
- import { tabActiveClassname, tabPanelActiveClassname } from './const';
5
- import { tabPanelType, tabType, tabsListType, tabsType } from '.';
3
+ import { TabAttrs, TabPanelAttrs, TabsAttrs, tabActiveClassname, tabPanelActiveClassname, tabPanelType, tabType, tabsListType, tabsType, } from './const';
6
4
  export const createYfmTabsCommand = (state, dispatch) => {
7
5
  if (dispatch) {
8
6
  const { schema } = state;
@@ -1,5 +1,8 @@
1
- export { TabsNode, tabType, tabPanelType, tabsListType, tabsType } from './YfmTabsSpecs';
2
- export declare const tabActiveClassname = "yfm-tab active";
1
+ export * from './YfmTabsSpecs/const';
2
+ export { tabType, tabPanelType, tabsListType, tabsType } from './YfmTabsSpecs';
3
+ export declare const YFM_TAB_CLASSNAME = "yfm-tab";
4
+ export declare const DIPLODOC_ID_ATTR = "data-diplodoc-id";
5
+ export declare const tabActiveClassname: string;
3
6
  export declare const tabInactiveClassname = "yfm-tab";
4
7
  export declare const tabPanelActiveClassname = "yfm-tab-panel active";
5
8
  export declare const tabPanelInactiveClassname = "yfm-tab-panel";
@@ -1,5 +1,8 @@
1
- export { TabsNode, tabType, tabPanelType, tabsListType, tabsType } from './YfmTabsSpecs';
2
- export const tabActiveClassname = 'yfm-tab active';
3
- export const tabInactiveClassname = 'yfm-tab';
1
+ export * from './YfmTabsSpecs/const';
2
+ export { tabType, tabPanelType, tabsListType, tabsType } from './YfmTabsSpecs';
3
+ export const YFM_TAB_CLASSNAME = 'yfm-tab';
4
+ export const DIPLODOC_ID_ATTR = 'data-diplodoc-id';
5
+ export const tabActiveClassname = `${YFM_TAB_CLASSNAME} active`;
6
+ export const tabInactiveClassname = YFM_TAB_CLASSNAME;
4
7
  export const tabPanelActiveClassname = 'yfm-tab-panel active';
5
8
  export const tabPanelInactiveClassname = 'yfm-tab-panel';
@@ -1,5 +1,5 @@
1
- import { Command, Plugin } from 'prosemirror-state';
2
- import { NodeWithPos } from 'prosemirror-utils';
1
+ import { type Command, Plugin } from 'prosemirror-state';
2
+ import { type NodeWithPos } from 'prosemirror-utils';
3
3
  export declare const dragAutoSwitch: () => Plugin<any>;
4
4
  export declare const tabPanelArrowDown: Command;
5
5
  export declare const tabEnter: Command;
@@ -8,8 +8,8 @@ import { findChildIndex } from '../../../table-utils/helpers';
8
8
  import { isSameNodeType } from '../../../utils';
9
9
  import { get$Cursor, isTextSelection } from '../../../utils/selection';
10
10
  import { TabAttrs, TabPanelAttrs } from './YfmTabsSpecs/const';
11
- import { tabActiveClassname, tabInactiveClassname, tabPanelActiveClassname, tabPanelInactiveClassname, tabPanelType, tabType, tabsListType, tabsType, } from './const';
12
- import { atEndOfPanel } from './utils';
11
+ import { tabInactiveClassname, tabPanelActiveClassname, tabPanelInactiveClassname, tabPanelType, tabType, tabsListType, tabsType, } from './const';
12
+ import { atEndOfPanel, execAfterPaint, switchTabByElem, switchTabById } from './utils';
13
13
  export const dragAutoSwitch = () => new Plugin({
14
14
  view: TabsAutoSwitchOnDragOver.view,
15
15
  });
@@ -35,12 +35,12 @@ class TabsAutoSwitchOnDragOver {
35
35
  const pos = view.posAtCoords({ left: event.clientX, top: event.clientY });
36
36
  if (pos) {
37
37
  const elem = findDomRefAtPos(pos.pos, view.domAtPos.bind(view));
38
- const cutElem = elem.closest(TabsAutoSwitchOnDragOver.TAB_SELECTOR);
39
- if (cutElem === this._tabElem)
38
+ const tabElem = elem.closest(TabsAutoSwitchOnDragOver.TAB_SELECTOR);
39
+ if (tabElem === this._tabElem)
40
40
  return;
41
41
  this._clear();
42
- if (cutElem)
43
- this._setTabElem(cutElem);
42
+ if (tabElem)
43
+ this._setTabElem(tabElem);
44
44
  }
45
45
  }
46
46
  _clear() {
@@ -55,29 +55,7 @@ class TabsAutoSwitchOnDragOver {
55
55
  }
56
56
  _switchTab() {
57
57
  if (this._editorView.dragging && this._tabElem) {
58
- const pos = this._editorView.posAtDOM(this._tabElem, 0, -1);
59
- const $pos = this._editorView.state.doc.resolve(pos);
60
- const { state } = this._editorView;
61
- let { depth } = $pos;
62
- let tabId = '';
63
- let tabsNode = null;
64
- do {
65
- const node = $pos.node(depth);
66
- if (node.type === tabType(state.schema)) {
67
- tabId = node.attrs[TabAttrs.dataDiplodocid];
68
- continue;
69
- }
70
- if (node.type === tabsType(state.schema)) {
71
- tabsNode = { node, pos: $pos.before(depth) };
72
- break;
73
- }
74
- } while (--depth >= 0);
75
- if (tabId && tabsNode) {
76
- const { tr } = state;
77
- if (switchYfmTab(tabsNode, tabId, tr)) {
78
- this._editorView.dispatch(tr.setMeta('addToHistory', false));
79
- }
80
- }
58
+ switchTabByElem(this._tabElem);
81
59
  }
82
60
  this._clear();
83
61
  }
@@ -87,49 +65,6 @@ TabsAutoSwitchOnDragOver.TAB_SELECTOR = '.yfm-tab:not([data-diplodoc-is-active=t
87
65
  TabsAutoSwitchOnDragOver.OPEN_TIMEOUT = 500; //ms
88
66
  TabsAutoSwitchOnDragOver.THROTTLE_WAIT = 50; //ms
89
67
  TabsAutoSwitchOnDragOver.view = (view) => new _a(view);
90
- function switchYfmTab({ node: tabsNode, pos: tabsPos }, tabId, tr) {
91
- const { schema } = tabsNode.type;
92
- if (tabsNode.type !== tabsType(schema))
93
- return false;
94
- const tabsList = tabsNode.firstChild;
95
- if ((tabsList === null || tabsList === void 0 ? void 0 : tabsList.type) !== tabsListType(schema))
96
- return false;
97
- const tabsListPos = tabsPos + 1;
98
- let panelId = null;
99
- tabsList.forEach((node, offset) => {
100
- if (node.type !== tabType(schema))
101
- return;
102
- const tabPos = tabsListPos + 1 + offset;
103
- const tabAttrs = Object.assign(Object.assign({}, node.attrs), { [TabAttrs.ariaSelected]: 'false', [TabAttrs.dataDiplodocIsActive]: 'false' });
104
- if (node.attrs[TabAttrs.dataDiplodocid] === tabId) {
105
- panelId = node.attrs[TabAttrs.ariaControls];
106
- tabAttrs[TabAttrs.ariaSelected] = 'true';
107
- tabAttrs[TabAttrs.dataDiplodocIsActive] = 'true';
108
- }
109
- tr.setNodeMarkup(tabPos, null, tabAttrs);
110
- });
111
- if (!panelId)
112
- return false;
113
- tabsNode.forEach((node, offset) => {
114
- var _b;
115
- if (node.type !== tabPanelType(schema))
116
- return;
117
- const tabPanelPos = tabsPos + 1 + offset;
118
- const tabPanelAttrs = Object.assign({}, node.attrs);
119
- const tabPanelClassList = new Set(((_b = node.attrs[TabPanelAttrs.class]) !== null && _b !== void 0 ? _b : '')
120
- .split(' ')
121
- .filter((val) => Boolean(val.trim())));
122
- if (node.attrs[TabPanelAttrs.id] === panelId) {
123
- tabPanelClassList.add('active');
124
- }
125
- else {
126
- tabPanelClassList.delete('active');
127
- }
128
- tabPanelAttrs[TabPanelAttrs.class] = Array.from(tabPanelClassList).join(' ');
129
- tr.setNodeMarkup(tabPanelPos, null, tabPanelAttrs);
130
- });
131
- return true;
132
- }
133
68
  export const tabPanelArrowDown = (state, dispatch, view) => {
134
69
  var _b;
135
70
  const { selection: sel } = state;
@@ -182,22 +117,6 @@ export const liftEmptyBlockFromTabPanel = (state, dispatch) => {
182
117
  }
183
118
  return false;
184
119
  };
185
- const makeTabsInactive = (tabNodes, tabPanels, tr) => {
186
- // Find all active tabs and make them inactive
187
- const activeTabs = tabNodes.filter((v) => v.node.attrs[TabAttrs.dataDiplodocIsActive] === 'true');
188
- if (activeTabs.length) {
189
- activeTabs.forEach((tab) => {
190
- tr.setNodeMarkup(tab.pos, null, Object.assign(Object.assign({}, tab.node.attrs), { class: tabInactiveClassname, [TabAttrs.dataDiplodocIsActive]: 'false' }));
191
- });
192
- }
193
- // Find all active panels and make them inactive
194
- const activePanels = tabPanels.filter((v) => v.node.attrs[TabPanelAttrs.class] === tabPanelActiveClassname);
195
- if (activePanels.length) {
196
- activePanels.forEach((tabPanel) => {
197
- tr.setNodeMarkup(tr.mapping.map(tabPanel.pos), null, Object.assign(Object.assign({}, tabPanel.node.attrs), { class: tabPanelInactiveClassname }));
198
- });
199
- }
200
- };
201
120
  export const createTab = (afterTab, tabsParentNode) => (state, dispatch, view) => {
202
121
  const tabNodes = findChildren(tabsParentNode.node, (node) => node.type.name === tabType(state.schema).name);
203
122
  const tabPanels = findChildren(tabsParentNode.node, (tabNode) => {
@@ -210,14 +129,14 @@ export const createTab = (afterTab, tabsParentNode) => (state, dispatch, view) =
210
129
  const newPanel = tabPanelType(state.schema).create({
211
130
  [TabPanelAttrs.ariaLabelledby]: tabId,
212
131
  [TabPanelAttrs.id]: panelId,
213
- [TabPanelAttrs.class]: tabPanelActiveClassname,
132
+ [TabPanelAttrs.class]: tabPanelInactiveClassname,
214
133
  }, pType(state.schema).createAndFill());
215
134
  const newTab = tabType(state.schema).create({
216
135
  [TabAttrs.id]: tabId,
217
136
  [TabAttrs.dataDiplodocid]: tabId,
218
137
  [TabAttrs.dataDiplodocKey]: tabId,
219
- [TabAttrs.dataDiplodocIsActive]: 'true',
220
- [TabAttrs.class]: tabActiveClassname,
138
+ [TabAttrs.dataDiplodocIsActive]: 'false',
139
+ [TabAttrs.class]: tabInactiveClassname,
221
140
  [TabAttrs.role]: 'tab',
222
141
  [TabAttrs.ariaControls]: panelId,
223
142
  });
@@ -229,12 +148,14 @@ export const createTab = (afterTab, tabsParentNode) => (state, dispatch, view) =
229
148
  tabPanels.forEach((v) => {
230
149
  v.pos = v.pos + tabsParentNode.pos + 1;
231
150
  });
232
- makeTabsInactive(tabNodes, tabPanels, tr);
233
151
  dispatch === null || dispatch === void 0 ? void 0 : dispatch(tr
234
152
  .insert(afterPanelNode.pos + afterPanelNode.node.nodeSize, newPanel)
235
153
  .insert(afterTab.pos + afterTab.node.nodeSize, newTab)
236
154
  .setSelection(TextSelection.create(tr.doc, afterTab.pos + afterTab.node.nodeSize + 1)));
237
155
  view === null || view === void 0 ? void 0 : view.focus();
156
+ if (view) {
157
+ execAfterPaint(() => switchTabById(view.dom, tabId));
158
+ }
238
159
  return true;
239
160
  };
240
161
  export const removeTab = (tabToRemove, tabsParentNode) => (state, dispatch, view) => {
@@ -263,18 +184,16 @@ export const removeTab = (tabToRemove, tabsParentNode) => (state, dispatch, view
263
184
  v.pos = v.pos + tabsParentNode.pos + 1;
264
185
  });
265
186
  const newTabNode = tabNodes[newTabIdx];
266
- const newTabPanelNode = tabPanels[newTabIdx];
267
- makeTabsInactive(tabNodes, tabPanels, tr);
187
+ const newActiveTabId = newTabNode.node.attrs[TabAttrs.dataDiplodocid];
268
188
  tr
269
189
  // Delete panel
270
190
  .delete(panelToRemove.pos, panelToRemove.pos + panelToRemove.node.nodeSize)
271
191
  // Delete tab
272
192
  .delete(tabToRemove.pos, tabToRemove.pos + tabToRemove.node.nodeSize)
273
- // Set new active tab
274
- .setNodeMarkup(tr.mapping.map(newTabNode.pos), null, Object.assign(Object.assign({}, newTabNode.node.attrs), { class: tabActiveClassname, [TabAttrs.dataDiplodocIsActive]: 'true' }))
275
- // Set new active panel
276
- .setNodeMarkup(tr.mapping.map(newTabPanelNode.pos), null, Object.assign(Object.assign({}, newTabPanelNode.node.attrs), { class: tabPanelActiveClassname }))
277
193
  .setSelection(TextSelection.create(tr.doc, tr.mapping.map(newTabNode.pos + newTabNode.node.nodeSize - 1)));
194
+ // Set new active tab
195
+ if (view)
196
+ execAfterPaint(() => switchTabById(view.dom, newActiveTabId));
278
197
  }
279
198
  dispatch(tr);
280
199
  view === null || view === void 0 ? void 0 : view.focus();
@@ -1,2 +1,5 @@
1
- import { EditorView } from 'prosemirror-view';
1
+ import type { EditorView } from 'prosemirror-view';
2
+ export declare const execAfterPaint: (fn: () => void) => void;
2
3
  export declare const atEndOfPanel: (view?: EditorView) => number | false | null;
4
+ export declare const switchTabByElem: (tabElem: HTMLElement) => void;
5
+ export declare const switchTabById: (container: HTMLElement, tabId: string) => void;
@@ -1,4 +1,9 @@
1
- import { tabPanelType } from '.';
1
+ import { DIPLODOC_ID_ATTR, YFM_TAB_CLASSNAME, tabPanelType } from './const';
2
+ export const execAfterPaint = (fn) => {
3
+ requestAnimationFrame(() => {
4
+ requestAnimationFrame(fn);
5
+ });
6
+ };
2
7
  export const atEndOfPanel = (view) => {
3
8
  if (!view)
4
9
  return null;
@@ -14,3 +19,15 @@ export const atEndOfPanel = (view) => {
14
19
  }
15
20
  return null;
16
21
  };
22
+ export const switchTabByElem = (tabElem) => {
23
+ if (tabElem.classList.contains(YFM_TAB_CLASSNAME)) {
24
+ tabElem.click();
25
+ }
26
+ };
27
+ export const switchTabById = (container, tabId) => {
28
+ const selector = `.${YFM_TAB_CLASSNAME}[${DIPLODOC_ID_ATTR}="${tabId}"]`;
29
+ const tabElem = container.querySelector(selector);
30
+ if (tabElem) {
31
+ switchTabByElem(tabElem);
32
+ }
33
+ };
@@ -1,4 +1,4 @@
1
- import { NodeViewConstructor } from 'prosemirror-view';
1
+ import { type NodeViewConstructor } from 'prosemirror-view';
2
2
  import './index.css';
3
3
  export declare const tabView: NodeViewConstructor;
4
4
  export declare const tabPanelView: NodeViewConstructor;
@@ -1,16 +1,16 @@
1
+ import { TextSelection } from 'prosemirror-state';
1
2
  import { findParentNodeOfTypeClosestToPos } from 'prosemirror-utils';
2
3
  import { cn } from '../../../classname';
4
+ import { tabType, tabsType } from './const';
3
5
  import { crossSvg, plusSvg } from './icons';
4
6
  import { createTab, removeTab } from './plugins';
5
- import { tabType, tabsType } from '.';
7
+ import { execAfterPaint } from './utils';
6
8
  import './index.css';
7
9
  const cnYfmTab = cn('yfm-tab');
8
- const ignoreMutation = (node, view, getPos) => (mutation) => {
10
+ const ignoreMutation = (_node, _view, _getPos) => (mutation) => {
9
11
  if (mutation instanceof MutationRecord &&
10
12
  mutation.type === 'attributes' &&
11
13
  mutation.attributeName) {
12
- const newAttr = mutation.target.getAttribute(mutation.attributeName);
13
- view.dispatch(view.state.tr.setNodeMarkup(getPos(), null, Object.assign(Object.assign({}, node.attrs), { [mutation.attributeName]: newAttr })));
14
14
  return true;
15
15
  }
16
16
  return false;
@@ -29,6 +29,26 @@ export const tabView = (node, view, getPos) => {
29
29
  wrapperElem.addEventListener('click', () => {
30
30
  // Click on parent node to trigger event listener that selects current tab
31
31
  tabElem.click();
32
+ {
33
+ /**
34
+ * Hack for empty tabs
35
+ *
36
+ * Problem: when clicking on an empty tab (without text content) it focuses, and selection doesn't move to beginning of tab
37
+ *
38
+ * Temporary fix: manually return focus to pm-view, move text selection to beginning of tab
39
+ */
40
+ view.focus();
41
+ // tab is empty
42
+ if (node.nodeSize < 3) {
43
+ execAfterPaint(() => {
44
+ const pos = getPos();
45
+ if (pos !== undefined) {
46
+ const { tr } = view.state;
47
+ view.dispatch(tr.setSelection(TextSelection.create(tr.doc, pos + 1)).scrollIntoView());
48
+ }
49
+ });
50
+ }
51
+ }
32
52
  });
33
53
  const removeTabButton = document.createElement('div');
34
54
  removeTabButton.setAttribute('class', cnYfmTab('remove-button'));
@@ -1,2 +1,2 @@
1
1
  /** During build process, the current version will be injected here */
2
- export const VERSION = typeof '14.10.5' !== 'undefined' ? '14.10.5' : 'unknown';
2
+ export const VERSION = typeof '14.11.0' !== 'undefined' ? '14.11.0' : 'unknown';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gravity-ui/markdown-editor",
3
- "version": "14.10.5",
3
+ "version": "14.11.0",
4
4
  "description": "Markdown wysiwyg and markup editor",
5
5
  "license": "MIT",
6
6
  "repository": {