@gravity-ui/markdown-editor 15.34.4 → 15.35.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.
- package/build/cjs/bundle/MarkupEditorView.js +1 -1
- package/build/cjs/bundle/MarkupEditorView.js.map +1 -1
- package/build/cjs/bundle/ToolbarView.d.ts +1 -2
- package/build/cjs/bundle/ToolbarView.js +22 -10
- package/build/cjs/bundle/ToolbarView.js.map +1 -1
- package/build/cjs/bundle/WysiwygEditorView.d.ts +1 -1
- package/build/cjs/bundle/WysiwygEditorView.js +4 -4
- package/build/cjs/bundle/WysiwygEditorView.js.map +1 -1
- package/build/cjs/bundle/config/markup.js +1 -0
- package/build/cjs/bundle/config/markup.js.map +1 -1
- package/build/cjs/bundle/config/wysiwyg.js +2 -0
- package/build/cjs/bundle/config/wysiwyg.js.map +1 -1
- package/build/cjs/bundle/emoji.js +3 -0
- package/build/cjs/bundle/emoji.js.map +1 -1
- package/build/cjs/bundle/toolbar/wysiwyg/WToolbarColors.js +36 -2
- package/build/cjs/bundle/toolbar/wysiwyg/WToolbarColors.js.map +1 -1
- package/build/cjs/extensions/behavior/Placeholder/index.js +54 -20
- package/build/cjs/extensions/behavior/Placeholder/index.js.map +1 -1
- package/build/cjs/extensions/markdown/Lists/commands.d.ts +1 -0
- package/build/cjs/extensions/markdown/Lists/commands.js +14 -0
- package/build/cjs/extensions/markdown/Lists/commands.js.map +1 -1
- package/build/cjs/extensions/markdown/Lists/index.js +1 -0
- package/build/cjs/extensions/markdown/Lists/index.js.map +1 -1
- package/build/cjs/extensions/yfm/YfmTable/plugins/YfmTableControls/dnd/dnd-auto-scroll.d.ts +27 -0
- package/build/cjs/extensions/yfm/YfmTable/plugins/YfmTableControls/dnd/dnd-auto-scroll.js +81 -0
- package/build/cjs/extensions/yfm/YfmTable/plugins/YfmTableControls/dnd/dnd-auto-scroll.js.map +1 -0
- package/build/cjs/extensions/yfm/YfmTable/plugins/YfmTableControls/dnd/dnd.js +33 -0
- package/build/cjs/extensions/yfm/YfmTable/plugins/YfmTableControls/dnd/dnd.js.map +1 -1
- package/build/cjs/modules/toolbars/items.js +2 -0
- package/build/cjs/modules/toolbars/items.js.map +1 -1
- package/build/cjs/modules/toolbars/types.d.ts +1 -0
- package/build/cjs/modules/toolbars/types.js.map +1 -1
- package/build/cjs/react-utils/memo.d.ts +1 -0
- package/build/cjs/react-utils/memo.js +8 -0
- package/build/cjs/react-utils/memo.js.map +1 -0
- package/build/cjs/styles/markdown.css +1 -1
- package/build/cjs/styles/styles.css +5 -0
- package/build/cjs/styles/yfm-overrides.css +1 -1
- package/build/cjs/toolbar/Toolbar.d.ts +4 -0
- package/build/cjs/toolbar/Toolbar.js +9 -4
- package/build/cjs/toolbar/Toolbar.js.map +1 -1
- package/build/cjs/toolbar/ToolbarButton.js +3 -3
- package/build/cjs/toolbar/ToolbarButton.js.map +1 -1
- package/build/cjs/toolbar/ToolbarButtonPopup.js +3 -3
- package/build/cjs/toolbar/ToolbarButtonPopup.js.map +1 -1
- package/build/cjs/toolbar/ToolbarGroup.js +5 -4
- package/build/cjs/toolbar/ToolbarGroup.js.map +1 -1
- package/build/cjs/toolbar/ToolbarListButton.js +9 -6
- package/build/cjs/toolbar/ToolbarListButton.js.map +1 -1
- package/build/cjs/toolbar/ToolbarRerender.d.ts +3 -0
- package/build/cjs/toolbar/ToolbarRerender.js +48 -0
- package/build/cjs/toolbar/ToolbarRerender.js.map +1 -0
- package/build/cjs/toolbar/context.d.ts +10 -0
- package/build/cjs/toolbar/context.js +12 -0
- package/build/cjs/toolbar/context.js.map +1 -0
- package/build/cjs/toolbar/hooks.d.ts +8 -0
- package/build/cjs/toolbar/hooks.js +68 -0
- package/build/cjs/toolbar/hooks.js.map +1 -0
- package/build/cjs/toolbar/index.d.ts +1 -0
- package/build/cjs/toolbar/index.js +4 -0
- package/build/cjs/toolbar/index.js.map +1 -1
- package/build/cjs/toolbar/types.d.ts +1 -0
- package/build/cjs/toolbar/types.js.map +1 -1
- package/build/cjs/version.js +1 -1
- package/build/cjs/version.js.map +1 -1
- package/build/cjs/view/hocs/withMermaid/index.js +6 -1
- package/build/cjs/view/hocs/withMermaid/index.js.map +1 -1
- package/build/esm/bundle/MarkupEditorView.js +1 -1
- package/build/esm/bundle/MarkupEditorView.js.map +1 -1
- package/build/esm/bundle/ToolbarView.d.ts +1 -2
- package/build/esm/bundle/ToolbarView.js +24 -12
- package/build/esm/bundle/ToolbarView.js.map +1 -1
- package/build/esm/bundle/WysiwygEditorView.d.ts +1 -1
- package/build/esm/bundle/WysiwygEditorView.js +3 -4
- package/build/esm/bundle/WysiwygEditorView.js.map +1 -1
- package/build/esm/bundle/config/markup.js +1 -0
- package/build/esm/bundle/config/markup.js.map +1 -1
- package/build/esm/bundle/config/wysiwyg.js +2 -0
- package/build/esm/bundle/config/wysiwyg.js.map +1 -1
- package/build/esm/bundle/emoji.js +3 -0
- package/build/esm/bundle/emoji.js.map +1 -1
- package/build/esm/bundle/toolbar/wysiwyg/WToolbarColors.js +36 -2
- package/build/esm/bundle/toolbar/wysiwyg/WToolbarColors.js.map +1 -1
- package/build/esm/extensions/behavior/Placeholder/index.js +55 -21
- package/build/esm/extensions/behavior/Placeholder/index.js.map +1 -1
- package/build/esm/extensions/markdown/Lists/commands.d.ts +1 -0
- package/build/esm/extensions/markdown/Lists/commands.js +13 -0
- package/build/esm/extensions/markdown/Lists/commands.js.map +1 -1
- package/build/esm/extensions/markdown/Lists/index.js +2 -1
- package/build/esm/extensions/markdown/Lists/index.js.map +1 -1
- package/build/esm/extensions/yfm/YfmTable/plugins/YfmTableControls/dnd/dnd-auto-scroll.d.ts +27 -0
- package/build/esm/extensions/yfm/YfmTable/plugins/YfmTableControls/dnd/dnd-auto-scroll.js +77 -0
- package/build/esm/extensions/yfm/YfmTable/plugins/YfmTableControls/dnd/dnd-auto-scroll.js.map +1 -0
- package/build/esm/extensions/yfm/YfmTable/plugins/YfmTableControls/dnd/dnd.js +33 -0
- package/build/esm/extensions/yfm/YfmTable/plugins/YfmTableControls/dnd/dnd.js.map +1 -1
- package/build/esm/modules/toolbars/items.js +2 -0
- package/build/esm/modules/toolbars/items.js.map +1 -1
- package/build/esm/modules/toolbars/types.d.ts +1 -0
- package/build/esm/modules/toolbars/types.js.map +1 -1
- package/build/esm/react-utils/memo.d.ts +1 -0
- package/build/esm/react-utils/memo.js +5 -0
- package/build/esm/react-utils/memo.js.map +1 -0
- package/build/esm/styles/markdown.css +1 -1
- package/build/esm/styles/styles.css +5 -0
- package/build/esm/styles/yfm-overrides.css +1 -1
- package/build/esm/toolbar/Toolbar.d.ts +4 -0
- package/build/esm/toolbar/Toolbar.js +9 -4
- package/build/esm/toolbar/Toolbar.js.map +1 -1
- package/build/esm/toolbar/ToolbarButton.js +3 -3
- package/build/esm/toolbar/ToolbarButton.js.map +1 -1
- package/build/esm/toolbar/ToolbarButtonPopup.js +3 -3
- package/build/esm/toolbar/ToolbarButtonPopup.js.map +1 -1
- package/build/esm/toolbar/ToolbarGroup.js +5 -4
- package/build/esm/toolbar/ToolbarGroup.js.map +1 -1
- package/build/esm/toolbar/ToolbarListButton.js +9 -6
- package/build/esm/toolbar/ToolbarListButton.js.map +1 -1
- package/build/esm/toolbar/ToolbarRerender.d.ts +3 -0
- package/build/esm/toolbar/ToolbarRerender.js +44 -0
- package/build/esm/toolbar/ToolbarRerender.js.map +1 -0
- package/build/esm/toolbar/context.d.ts +10 -0
- package/build/esm/toolbar/context.js +8 -0
- package/build/esm/toolbar/context.js.map +1 -0
- package/build/esm/toolbar/hooks.d.ts +8 -0
- package/build/esm/toolbar/hooks.js +64 -0
- package/build/esm/toolbar/hooks.js.map +1 -0
- package/build/esm/toolbar/index.d.ts +1 -0
- package/build/esm/toolbar/index.js +1 -0
- package/build/esm/toolbar/index.js.map +1 -1
- package/build/esm/toolbar/types.d.ts +1 -0
- package/build/esm/toolbar/types.js.map +1 -1
- package/build/esm/version.js +1 -1
- package/build/esm/version.js.map +1 -1
- package/build/esm/view/hocs/withMermaid/index.js +6 -1
- package/build/esm/view/hocs/withMermaid/index.js.map +1 -1
- package/build/styles.css +7 -2
- package/package.json +17 -79
- package/README.md +0 -89
|
@@ -1,10 +1,44 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useState } from 'react';
|
|
3
|
+
import { useLatest } from 'react-use';
|
|
4
|
+
import { isEqual } from "../../../lodash.js";
|
|
5
|
+
import { useToolbarContext } from "../../../toolbar/context.js";
|
|
2
6
|
import { ToolbarColors } from "../custom/ToolbarColors.js";
|
|
3
7
|
export const WToolbarColors = ({ disablePortal, className, editor, focus, onClick, }) => {
|
|
8
|
+
const { active, enabled, currentColor } = useColorsState(editor);
|
|
4
9
|
const action = editor.actions.colorify;
|
|
5
|
-
|
|
6
|
-
return (_jsx(ToolbarColors, { active: action.isActive(), enable: action.isEnable(), currentColor: currentColor, exec: (color) => {
|
|
10
|
+
return (_jsx(ToolbarColors, { active: active, enable: enabled, currentColor: currentColor, exec: (color) => {
|
|
7
11
|
action.run({ color: color === currentColor ? '' : color });
|
|
8
12
|
}, disablePortal: disablePortal, className: className, focus: focus, onClick: onClick, withDefault: true }));
|
|
9
13
|
};
|
|
14
|
+
function useColorsState(editor) {
|
|
15
|
+
const action = editor.actions.colorify;
|
|
16
|
+
const context = useToolbarContext();
|
|
17
|
+
const [state, setState] = useState({
|
|
18
|
+
active: false,
|
|
19
|
+
enabled: true,
|
|
20
|
+
currentColor: '',
|
|
21
|
+
});
|
|
22
|
+
const stateRef = useLatest(state);
|
|
23
|
+
useEffect(() => {
|
|
24
|
+
if (!context)
|
|
25
|
+
return undefined;
|
|
26
|
+
const onUpdate = () => {
|
|
27
|
+
const newState = {
|
|
28
|
+
active: action.isActive(),
|
|
29
|
+
enabled: action.isEnable(),
|
|
30
|
+
currentColor: action.meta(),
|
|
31
|
+
};
|
|
32
|
+
if (!isEqual(stateRef.current, newState)) {
|
|
33
|
+
setState(newState);
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
onUpdate();
|
|
37
|
+
context.eventBus.on('update', onUpdate);
|
|
38
|
+
return () => {
|
|
39
|
+
context.eventBus.off('update', onUpdate);
|
|
40
|
+
};
|
|
41
|
+
}, [action, context, stateRef]);
|
|
42
|
+
return state;
|
|
43
|
+
}
|
|
10
44
|
//# sourceMappingURL=WToolbarColors.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WToolbarColors.js","sourceRoot":"../../../../../src","sources":["bundle/toolbar/wysiwyg/WToolbarColors.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAC,aAAa,EAA0B,mCAAgC;AAK/E,MAAM,CAAC,MAAM,cAAc,GAAkC,CAAC,EAC1D,aAAa,EACb,SAAS,EACT,MAAM,EACN,KAAK,EACL,OAAO,GACV,EAAE,EAAE;IACD,MAAM,MAAM,GAAG,
|
|
1
|
+
{"version":3,"file":"WToolbarColors.js","sourceRoot":"../../../../../src","sources":["bundle/toolbar/wysiwyg/WToolbarColors.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAC,SAAS,EAAE,QAAQ,EAAC,MAAM,OAAO,CAAC;AAE1C,OAAO,EAAC,SAAS,EAAC,MAAM,WAAW,CAAC;AAGpC,OAAO,EAAC,OAAO,EAAC,2BAAmB;AACnC,OAAO,EAAC,iBAAiB,EAAC,oCAA4B;AAEtD,OAAO,EAAC,aAAa,EAA0B,mCAAgC;AAK/E,MAAM,CAAC,MAAM,cAAc,GAAkC,CAAC,EAC1D,aAAa,EACb,SAAS,EACT,MAAM,EACN,KAAK,EACL,OAAO,GACV,EAAE,EAAE;IACD,MAAM,EAAC,MAAM,EAAE,OAAO,EAAE,YAAY,EAAC,GAAG,cAAc,CAAC,MAAM,CAAC,CAAC;IAC/D,MAAM,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC;IAEvC,OAAO,CACH,KAAC,aAAa,IACV,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,OAAO,EACf,YAAY,EAAE,YAAY,EAC1B,IAAI,EAAE,CAAC,KAAK,EAAE,EAAE;YACZ,MAAM,CAAC,GAAG,CAAC,EAAC,KAAK,EAAE,KAAK,KAAK,YAAY,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,EAAC,CAAC,CAAC;QAC7D,CAAC,EACD,aAAa,EAAE,aAAa,EAC5B,SAAS,EAAE,SAAS,EACpB,KAAK,EAAE,KAAK,EACZ,OAAO,EAAE,OAAO,EAChB,WAAW,SACb,CACL,CAAC;AACN,CAAC,CAAC;AAQF,SAAS,cAAc,CAAC,MAAqB;IACzC,MAAM,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC;IAEvC,MAAM,OAAO,GAAG,iBAAiB,EAAE,CAAC;IAEpC,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAc;QAC5C,MAAM,EAAE,KAAK;QACb,OAAO,EAAE,IAAI;QACb,YAAY,EAAE,EAAE;KACnB,CAAC,CAAC;IACH,MAAM,QAAQ,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC;IAElC,SAAS,CAAC,GAAG,EAAE;QACX,IAAI,CAAC,OAAO;YAAE,OAAO,SAAS,CAAC;QAE/B,MAAM,QAAQ,GAAG,GAAG,EAAE;YAClB,MAAM,QAAQ,GAAG;gBACb,MAAM,EAAE,MAAM,CAAC,QAAQ,EAAE;gBACzB,OAAO,EAAE,MAAM,CAAC,QAAQ,EAAE;gBAC1B,YAAY,EAAE,MAAM,CAAC,IAAI,EAAE;aAC9B,CAAC;YAEF,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC,EAAE,CAAC;gBACvC,QAAQ,CAAC,QAAQ,CAAC,CAAC;YACvB,CAAC;QACL,CAAC,CAAC;QAEF,QAAQ,EAAE,CAAC;QAEX,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;QAExC,OAAO,GAAG,EAAE;YACR,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;QAC7C,CAAC,CAAC;IACN,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC;IAEhC,OAAO,KAAK,CAAC;AACjB,CAAC","sourcesContent":["import {useEffect, useState} from 'react';\n\nimport {useLatest} from 'react-use';\n\nimport type {ActionStorage} from '#core';\nimport {isEqual} from 'src/lodash';\nimport {useToolbarContext} from 'src/toolbar/context';\n\nimport {ToolbarColors, type ToolbarColorsProps} from '../custom/ToolbarColors';\nimport type {WToolbarBaseProps} from '../types';\n\nexport type WToolbarColorsProps = WToolbarBaseProps & Pick<ToolbarColorsProps, 'disablePortal'>;\n\nexport const WToolbarColors: React.FC<WToolbarColorsProps> = ({\n disablePortal,\n className,\n editor,\n focus,\n onClick,\n}) => {\n const {active, enabled, currentColor} = useColorsState(editor);\n const action = editor.actions.colorify;\n\n return (\n <ToolbarColors\n active={active}\n enable={enabled}\n currentColor={currentColor}\n exec={(color) => {\n action.run({color: color === currentColor ? '' : color});\n }}\n disablePortal={disablePortal}\n className={className}\n focus={focus}\n onClick={onClick}\n withDefault\n />\n );\n};\n\ntype ColorsState = {\n active: boolean;\n enabled: boolean;\n currentColor: string;\n};\n\nfunction useColorsState(editor: ActionStorage): ColorsState {\n const action = editor.actions.colorify;\n\n const context = useToolbarContext();\n\n const [state, setState] = useState<ColorsState>({\n active: false,\n enabled: true,\n currentColor: '',\n });\n const stateRef = useLatest(state);\n\n useEffect(() => {\n if (!context) return undefined;\n\n const onUpdate = () => {\n const newState = {\n active: action.isActive(),\n enabled: action.isEnable(),\n currentColor: action.meta(),\n };\n\n if (!isEqual(stateRef.current, newState)) {\n setState(newState);\n }\n };\n\n onUpdate();\n\n context.eventBus.on('update', onUpdate);\n\n return () => {\n context.eventBus.off('update', onUpdate);\n };\n }, [action, context, stateRef]);\n\n return state;\n}\n"]}
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import { Plugin, PluginKey } from 'prosemirror-state';
|
|
2
2
|
// @ts-ignore // TODO: fix cjs build
|
|
3
|
-
import {
|
|
3
|
+
import { findParentNodeClosestToPos } from 'prosemirror-utils';
|
|
4
4
|
import { Decoration, DecorationSet } from 'prosemirror-view';
|
|
5
5
|
import { cn } from "../../../classname.js";
|
|
6
6
|
import { isEqual } from "../../../lodash.js";
|
|
7
|
-
import { isNodeEmpty } from "../../../utils/nodes.js";
|
|
8
7
|
import { getPlaceholderContent } from "../../../utils/placeholder.js";
|
|
9
8
|
import { isTextSelection } from "../../../utils/selection.js";
|
|
10
9
|
import "./index.css";
|
|
@@ -36,10 +35,32 @@ export const createPlaceholder = (node, parent, focus) => {
|
|
|
36
35
|
return placeholder;
|
|
37
36
|
};
|
|
38
37
|
const placeholderNeeded = (node) => {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
38
|
+
// Combine all checks in a single descendants pass
|
|
39
|
+
let hasAlwaysVisiblePlaceholder = false;
|
|
40
|
+
let hasNonEmptyContent = false;
|
|
41
|
+
node.descendants((n) => {
|
|
42
|
+
// Check for alwaysVisible placeholder
|
|
43
|
+
if (n.type.spec.placeholder?.alwaysVisible) {
|
|
44
|
+
hasAlwaysVisiblePlaceholder = true;
|
|
45
|
+
return false; // Stop traversal early
|
|
46
|
+
}
|
|
47
|
+
// Check if node has non-empty content (same logic as isNodeEmpty)
|
|
48
|
+
if (n.isAtom) {
|
|
49
|
+
hasNonEmptyContent = true;
|
|
50
|
+
return false; // Stop traversal early
|
|
51
|
+
}
|
|
52
|
+
if (n.isText && n.textContent) {
|
|
53
|
+
hasNonEmptyContent = true;
|
|
54
|
+
return false; // Stop traversal early
|
|
55
|
+
}
|
|
56
|
+
// Non-empty paragraph or other block with content
|
|
57
|
+
if (n.content.size > 0 && n.type.name !== 'paragraph') {
|
|
58
|
+
hasNonEmptyContent = true;
|
|
59
|
+
return false; // Stop traversal early
|
|
60
|
+
}
|
|
61
|
+
return true;
|
|
62
|
+
});
|
|
63
|
+
return !hasNonEmptyContent && !hasAlwaysVisiblePlaceholder;
|
|
43
64
|
};
|
|
44
65
|
const addDecoration = (widgetsMap, node, pos, parent, cursorPos, globalState) => {
|
|
45
66
|
const placeholderSpec = node.type.spec.placeholder;
|
|
@@ -87,19 +108,19 @@ export const Placeholder = (builder, opts) => {
|
|
|
87
108
|
},
|
|
88
109
|
}), builder.Priority.VeryHigh);
|
|
89
110
|
};
|
|
90
|
-
function getPlaceholderWidgetSpecs(state) {
|
|
111
|
+
function getPlaceholderWidgetSpecs(state, pluginKeys) {
|
|
91
112
|
const globalState = { hasFocus: false };
|
|
92
113
|
const widgetsMap = {};
|
|
93
114
|
const { selection } = state;
|
|
94
115
|
const cursorPos = isTextSelection(selection) ? selection.$cursor?.pos : null;
|
|
95
|
-
|
|
116
|
+
pluginKeys.forEach((f) => {
|
|
96
117
|
// We use find because it can be used to iterate over the DecorationSet.
|
|
97
118
|
f.getState(state)?.find(undefined, undefined, (spec) => {
|
|
98
119
|
widgetsMap[spec.pos] = f;
|
|
99
120
|
return false;
|
|
100
121
|
});
|
|
101
122
|
});
|
|
102
|
-
//
|
|
123
|
+
// Draw placeholder for all nodes where placeholder is alwaysVisible
|
|
103
124
|
const decorate = (node, pos, parent) => {
|
|
104
125
|
const placeholderSpec = node.type.spec.placeholder;
|
|
105
126
|
if (placeholderSpec && placeholderSpec.alwaysVisible && placeholderNeeded(node)) {
|
|
@@ -124,23 +145,36 @@ function getPlaceholderWidgetSpecs(state) {
|
|
|
124
145
|
return { widgetSpecs, hasFocus: globalState.hasFocus };
|
|
125
146
|
}
|
|
126
147
|
function initState(state) {
|
|
127
|
-
const
|
|
148
|
+
const pluginKeys = getPlaceholderPluginKeys(state.schema);
|
|
149
|
+
const { widgetSpecs, hasFocus } = getPlaceholderWidgetSpecs(state, pluginKeys);
|
|
128
150
|
const decorationSet = DecorationSet.create(state.doc, widgetSpecs.map((widget) => Decoration.widget(widget.pos, widget.toDOM, widget.spec)));
|
|
129
|
-
return { decorationSet, hasFocus };
|
|
151
|
+
return { decorationSet, hasFocus, pluginKeys };
|
|
130
152
|
}
|
|
131
|
-
function applyState(tr, oldPluginState,
|
|
132
|
-
|
|
153
|
+
function applyState(tr, oldPluginState, oldState, newState) {
|
|
154
|
+
// Early return if document hasn't changed and selection hasn't changed
|
|
155
|
+
// This avoids unnecessary recalculation of decorations
|
|
156
|
+
if (!tr.docChanged && !tr.selectionSet) {
|
|
157
|
+
return oldPluginState;
|
|
158
|
+
}
|
|
159
|
+
// Reuse cached plugin keys if schema hasn't changed
|
|
160
|
+
const pluginKeys = oldState.schema === newState.schema
|
|
161
|
+
? oldPluginState.pluginKeys
|
|
162
|
+
: getPlaceholderPluginKeys(newState.schema);
|
|
163
|
+
const { widgetSpecs, hasFocus } = getPlaceholderWidgetSpecs(newState, pluginKeys);
|
|
133
164
|
const { decorationSet } = oldPluginState;
|
|
134
165
|
const oldMappedSet = decorationSet.map(tr.mapping, tr.doc);
|
|
135
166
|
// Find all decorations that are present in old and new set
|
|
136
|
-
const
|
|
167
|
+
const unchangedPositions = new Set();
|
|
168
|
+
const decorationsThatDidNotChange = [];
|
|
169
|
+
widgetSpecs.forEach(({ pos, spec }) => {
|
|
137
170
|
const deco = oldMappedSet.find(pos, pos);
|
|
138
|
-
if (deco.length && isEqual(deco[0].spec, spec))
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
171
|
+
if (deco.length && isEqual(deco[0].spec, spec)) {
|
|
172
|
+
unchangedPositions.add(pos);
|
|
173
|
+
decorationsThatDidNotChange.push(...deco);
|
|
174
|
+
}
|
|
175
|
+
});
|
|
176
|
+
// Those are decorations that are present only in new set
|
|
177
|
+
const newAddedDecorations = widgetSpecs.filter(({ pos }) => !unchangedPositions.has(pos));
|
|
144
178
|
// That is a set with decorations that are present in old set and absent in new set
|
|
145
179
|
const notRelevantDecorations = oldMappedSet.remove(decorationsThatDidNotChange);
|
|
146
180
|
let newSet = oldMappedSet;
|
|
@@ -150,6 +184,6 @@ function applyState(tr, oldPluginState, _oldState, newState) {
|
|
|
150
184
|
// Add new decorations
|
|
151
185
|
if (newAddedDecorations.length)
|
|
152
186
|
newSet = newSet.add(tr.doc, newAddedDecorations.map((widget) => Decoration.widget(widget.pos, widget.toDOM, widget.spec)));
|
|
153
|
-
return { decorationSet: newSet, hasFocus };
|
|
187
|
+
return { decorationSet: newSet, hasFocus, pluginKeys };
|
|
154
188
|
}
|
|
155
189
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"../../../../../src","sources":["extensions/behavior/Placeholder/index.ts"],"names":[],"mappings":"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","sourcesContent":["import type {Node, Schema} from 'prosemirror-model';\nimport {type EditorState, Plugin, PluginKey, type Transaction} from 'prosemirror-state';\n// @ts-ignore // TODO: fix cjs build\nimport {findChildren, findParentNodeClosestToPos} from 'prosemirror-utils';\nimport {Decoration, DecorationSet} from 'prosemirror-view';\n\nimport {cn} from '../../../classname';\nimport type {ExtensionAuto} from '../../../core';\nimport {isEqual} from '../../../lodash';\nimport {isNodeEmpty} from '../../../utils/nodes';\nimport {type PlaceholderOptions, getPlaceholderContent} from '../../../utils/placeholder';\nimport {isTextSelection} from '../../../utils/selection';\n\nimport './index.scss';\n\nconst getPlaceholderPluginKeys = (schema: Schema) => {\n const pluginKeys = [];\n for (const node in schema.nodes) {\n if (schema.nodes[node]) {\n const spec = schema.nodes[node].spec;\n if (spec.placeholder?.customPlugin) {\n pluginKeys.push(spec.placeholder.customPlugin);\n }\n }\n }\n\n return pluginKeys;\n};\n\nconst b = cn('placeholder');\n\nexport const createPlaceholder = (node: Node, parent: Node | null, focus?: boolean) => {\n const content = getPlaceholderContent(node, parent);\n if (!content) return null;\n\n const placeholder = document.createElement('div');\n placeholder.className = b({focus});\n\n const placeholderCursor = document.createElement('span');\n placeholderCursor.className = b('cursor');\n\n const placeholderText = document.createElement('span');\n placeholderText.className = b('text');\n placeholderText.textContent = content;\n\n placeholder.append(placeholderCursor, placeholderText);\n\n return placeholder;\n};\n\nconst placeholderNeeded = (node: Node) => {\n const childrenWithPlaceholderVisible = findChildren(node, (n: Node) =>\n Boolean(n.type.spec.placeholder?.alwaysVisible),\n );\n\n return (\n isNodeEmpty(node) &&\n // If there are child nodes with constant placeholder - give them the priority\n !childrenWithPlaceholderVisible.length\n );\n};\n\nconst addDecoration = (\n widgetsMap: WidgetsMap,\n node: Node,\n pos: number,\n parent: Node | null,\n cursorPos: number | null | undefined,\n globalState: ApplyGlobalState,\n) => {\n const placeholderSpec = node.type.spec.placeholder;\n const decorationPosition = pos + node.childCount + 1;\n\n // We do not add decoration if there is already a placeholder at this position\n if (!placeholderSpec || widgetsMap[decorationPosition]) return;\n\n if (placeholderSpec.customPlugin) {\n widgetsMap[decorationPosition] = placeholderSpec.customPlugin;\n return;\n }\n\n const focus = decorationPosition === cursorPos;\n\n const placeholderDOM = createPlaceholder(node, parent, focus);\n if (!placeholderDOM) return;\n\n if (focus) globalState.hasFocus = true;\n\n widgetsMap[decorationPosition] = {\n pos: decorationPosition,\n toDOM: placeholderDOM,\n spec: {focus},\n };\n};\n\ntype ApplyGlobalState = {hasFocus: boolean};\n\ntype DecoWidgetParameters = Parameters<typeof Decoration.widget>;\ntype WidgetSpec = {\n pos: DecoWidgetParameters[0];\n toDOM: DecoWidgetParameters[1];\n spec?: DecoWidgetParameters[2];\n};\n\ntype PlaceholderPluginState = {decorationSet: DecorationSet; hasFocus: boolean};\n\ntype WidgetsMap = Record<number, WidgetSpec | PluginKey>;\n\nconst pluginKey = new PluginKey<PlaceholderPluginState>('placeholder_plugin');\n\nexport const Placeholder: ExtensionAuto<PlaceholderOptions> = (builder, opts) => {\n builder.context.set('placeholder', opts);\n builder.addPlugin(\n () =>\n new Plugin<PlaceholderPluginState>({\n key: pluginKey,\n props: {\n attributes(state) {\n const attrs: Record<string, string> = {};\n if (pluginKey.getState(state)!.hasFocus) {\n // hide native cursor\n attrs.class = 'g-md-editor-hidecursor';\n }\n return attrs;\n },\n decorations(state) {\n return pluginKey.getState(state)?.decorationSet;\n },\n },\n state: {\n init: (_config, state) => initState(state),\n apply: applyState,\n },\n }),\n builder.Priority.VeryHigh,\n );\n};\n\nfunction getPlaceholderWidgetSpecs(state: EditorState) {\n const globalState: ApplyGlobalState = {hasFocus: false};\n const widgetsMap: WidgetsMap = {};\n const {selection} = state;\n const cursorPos = isTextSelection(selection) ? selection.$cursor?.pos : null;\n\n getPlaceholderPluginKeys(state.schema).forEach((f) => {\n // We use find because it can be used to iterate over the DecorationSet.\n f.getState(state)?.find(undefined, undefined, (spec) => {\n widgetsMap[spec.pos] = f;\n return false;\n });\n });\n\n // Fraw placeholder for all nodes where placeholder is alwaysVisible\n const decorate = (node: Node, pos: number, parent: Node | null) => {\n const placeholderSpec = node.type.spec.placeholder;\n\n if (placeholderSpec && placeholderSpec.alwaysVisible && placeholderNeeded(node)) {\n addDecoration(widgetsMap, node, pos, parent, cursorPos, globalState);\n }\n };\n\n state.doc.descendants(decorate);\n\n const parentNode = findParentNodeClosestToPos(state.selection.$from, (node: Node) => {\n return Boolean(node.type.spec.placeholder);\n });\n\n const placeholderSpec = parentNode?.node.type.spec.placeholder;\n\n // Draw placeholder if it needs to be draw in the place of cursor\n if (\n parentNode &&\n placeholderNeeded(parentNode.node) &&\n placeholderSpec &&\n !placeholderSpec.alwaysVisible\n ) {\n const {node, pos, depth} = parentNode;\n const parent = depth > 0 ? state.selection.$from.node(depth - 1) : null;\n addDecoration(widgetsMap, node, pos, parent, cursorPos, globalState);\n }\n\n const widgetSpecs = Object.values(widgetsMap).filter(\n (decoration) => !(decoration instanceof PluginKey),\n ) as WidgetSpec[];\n\n return {widgetSpecs, hasFocus: globalState.hasFocus};\n}\n\nfunction initState(state: EditorState): PlaceholderPluginState {\n const {widgetSpecs, hasFocus} = getPlaceholderWidgetSpecs(state);\n const decorationSet = DecorationSet.create(\n state.doc,\n widgetSpecs.map((widget) => Decoration.widget(widget.pos, widget.toDOM, widget.spec)),\n );\n\n return {decorationSet, hasFocus};\n}\n\nfunction applyState(\n tr: Transaction,\n oldPluginState: PlaceholderPluginState,\n _oldState: EditorState,\n newState: EditorState,\n): PlaceholderPluginState {\n const {widgetSpecs, hasFocus} = getPlaceholderWidgetSpecs(newState);\n const {decorationSet} = oldPluginState;\n const oldMappedSet = decorationSet.map(tr.mapping, tr.doc);\n\n // Find all decorations that are present in old and new set\n const decorationsThatDidNotChange = widgetSpecs.reduce((a: Decoration[], {pos, spec}) => {\n const deco = oldMappedSet.find(pos, pos);\n if (deco.length && isEqual(deco[0].spec, spec)) a.push(...deco);\n return a;\n }, []);\n\n // Those are decorations that are presenr only in new set\n const newAddedDecorations = widgetSpecs.filter(\n ({pos}) => !decorationsThatDidNotChange.map(({from}) => from).includes(pos),\n );\n\n // That is a set with decorations that are present in old set and absent in new set\n const notRelevantDecorations = oldMappedSet.remove(decorationsThatDidNotChange);\n let newSet = oldMappedSet;\n // Remove decorations that are not present in new set\n if (notRelevantDecorations.find().length) newSet = newSet.remove(notRelevantDecorations.find());\n // Add new decorations\n if (newAddedDecorations.length)\n newSet = newSet.add(\n tr.doc,\n newAddedDecorations.map((widget) =>\n Decoration.widget(widget.pos, widget.toDOM, widget.spec),\n ),\n );\n\n return {decorationSet: newSet, hasFocus};\n}\n\ndeclare module 'prosemirror-model' {\n interface NodeSpec {\n placeholder?: {\n content: string | ((node: Node, parent?: Node | null) => string | null);\n customPlugin?: PluginKey<DecorationSet>;\n alwaysVisible?: boolean;\n };\n }\n}\n\ndeclare global {\n namespace WysiwygEditor {\n interface Context {\n placeholder: PlaceholderOptions;\n }\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"../../../../../src","sources":["extensions/behavior/Placeholder/index.ts"],"names":[],"mappings":"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","sourcesContent":["import type {Node, Schema} from 'prosemirror-model';\nimport {type EditorState, Plugin, PluginKey, type Transaction} from 'prosemirror-state';\n// @ts-ignore // TODO: fix cjs build\nimport {findParentNodeClosestToPos} from 'prosemirror-utils';\nimport {Decoration, DecorationSet} from 'prosemirror-view';\n\nimport {cn} from 'src/classname';\nimport type {ExtensionAuto} from 'src/core';\nimport {isEqual} from 'src/lodash';\nimport {type PlaceholderOptions, getPlaceholderContent} from 'src/utils/placeholder';\nimport {isTextSelection} from 'src/utils/selection';\n\nimport './index.scss';\n\nconst getPlaceholderPluginKeys = (schema: Schema) => {\n const pluginKeys = [];\n for (const node in schema.nodes) {\n if (schema.nodes[node]) {\n const spec = schema.nodes[node].spec;\n if (spec.placeholder?.customPlugin) {\n pluginKeys.push(spec.placeholder.customPlugin);\n }\n }\n }\n\n return pluginKeys;\n};\n\nconst b = cn('placeholder');\n\nexport const createPlaceholder = (node: Node, parent: Node | null, focus?: boolean) => {\n const content = getPlaceholderContent(node, parent);\n if (!content) return null;\n\n const placeholder = document.createElement('div');\n placeholder.className = b({focus});\n\n const placeholderCursor = document.createElement('span');\n placeholderCursor.className = b('cursor');\n\n const placeholderText = document.createElement('span');\n placeholderText.className = b('text');\n placeholderText.textContent = content;\n\n placeholder.append(placeholderCursor, placeholderText);\n\n return placeholder;\n};\n\nconst placeholderNeeded = (node: Node) => {\n // Combine all checks in a single descendants pass\n let hasAlwaysVisiblePlaceholder = false;\n let hasNonEmptyContent = false;\n\n node.descendants((n: Node) => {\n // Check for alwaysVisible placeholder\n if (n.type.spec.placeholder?.alwaysVisible) {\n hasAlwaysVisiblePlaceholder = true;\n return false; // Stop traversal early\n }\n\n // Check if node has non-empty content (same logic as isNodeEmpty)\n if (n.isAtom) {\n hasNonEmptyContent = true;\n return false; // Stop traversal early\n }\n\n if (n.isText && n.textContent) {\n hasNonEmptyContent = true;\n return false; // Stop traversal early\n }\n\n // Non-empty paragraph or other block with content\n if (n.content.size > 0 && n.type.name !== 'paragraph') {\n hasNonEmptyContent = true;\n return false; // Stop traversal early\n }\n\n return true;\n });\n\n return !hasNonEmptyContent && !hasAlwaysVisiblePlaceholder;\n};\n\nconst addDecoration = (\n widgetsMap: WidgetsMap,\n node: Node,\n pos: number,\n parent: Node | null,\n cursorPos: number | null | undefined,\n globalState: ApplyGlobalState,\n) => {\n const placeholderSpec = node.type.spec.placeholder;\n const decorationPosition = pos + node.childCount + 1;\n\n // We do not add decoration if there is already a placeholder at this position\n if (!placeholderSpec || widgetsMap[decorationPosition]) return;\n\n if (placeholderSpec.customPlugin) {\n widgetsMap[decorationPosition] = placeholderSpec.customPlugin;\n return;\n }\n\n const focus = decorationPosition === cursorPos;\n\n const placeholderDOM = createPlaceholder(node, parent, focus);\n if (!placeholderDOM) return;\n\n if (focus) globalState.hasFocus = true;\n\n widgetsMap[decorationPosition] = {\n pos: decorationPosition,\n toDOM: placeholderDOM,\n spec: {focus},\n };\n};\n\ntype ApplyGlobalState = {hasFocus: boolean};\n\ntype DecoWidgetParameters = Parameters<typeof Decoration.widget>;\ntype WidgetSpec = {\n pos: DecoWidgetParameters[0];\n toDOM: DecoWidgetParameters[1];\n spec?: DecoWidgetParameters[2];\n};\n\ntype PlaceholderPluginState = {\n decorationSet: DecorationSet;\n hasFocus: boolean;\n pluginKeys: PluginKey<DecorationSet>[];\n};\n\ntype WidgetsMap = Record<number, WidgetSpec | PluginKey>;\n\nconst pluginKey = new PluginKey<PlaceholderPluginState>('placeholder_plugin');\n\nexport const Placeholder: ExtensionAuto<PlaceholderOptions> = (builder, opts) => {\n builder.context.set('placeholder', opts);\n builder.addPlugin(\n () =>\n new Plugin<PlaceholderPluginState>({\n key: pluginKey,\n props: {\n attributes(state) {\n const attrs: Record<string, string> = {};\n if (pluginKey.getState(state)!.hasFocus) {\n // hide native cursor\n attrs.class = 'g-md-editor-hidecursor';\n }\n return attrs;\n },\n decorations(state) {\n return pluginKey.getState(state)?.decorationSet;\n },\n },\n state: {\n init: (_config, state) => initState(state),\n apply: applyState,\n },\n }),\n builder.Priority.VeryHigh,\n );\n};\n\nfunction getPlaceholderWidgetSpecs(state: EditorState, pluginKeys: PluginKey<DecorationSet>[]) {\n const globalState: ApplyGlobalState = {hasFocus: false};\n const widgetsMap: WidgetsMap = {};\n const {selection} = state;\n const cursorPos = isTextSelection(selection) ? selection.$cursor?.pos : null;\n\n pluginKeys.forEach((f) => {\n // We use find because it can be used to iterate over the DecorationSet.\n f.getState(state)?.find(undefined, undefined, (spec) => {\n widgetsMap[spec.pos] = f;\n return false;\n });\n });\n\n // Draw placeholder for all nodes where placeholder is alwaysVisible\n const decorate = (node: Node, pos: number, parent: Node | null) => {\n const placeholderSpec = node.type.spec.placeholder;\n\n if (placeholderSpec && placeholderSpec.alwaysVisible && placeholderNeeded(node)) {\n addDecoration(widgetsMap, node, pos, parent, cursorPos, globalState);\n }\n };\n\n state.doc.descendants(decorate);\n\n const parentNode = findParentNodeClosestToPos(state.selection.$from, (node: Node) => {\n return Boolean(node.type.spec.placeholder);\n });\n\n const placeholderSpec = parentNode?.node.type.spec.placeholder;\n\n // Draw placeholder if it needs to be draw in the place of cursor\n if (\n parentNode &&\n placeholderNeeded(parentNode.node) &&\n placeholderSpec &&\n !placeholderSpec.alwaysVisible\n ) {\n const {node, pos, depth} = parentNode;\n const parent = depth > 0 ? state.selection.$from.node(depth - 1) : null;\n addDecoration(widgetsMap, node, pos, parent, cursorPos, globalState);\n }\n\n const widgetSpecs = Object.values(widgetsMap).filter(\n (decoration) => !(decoration instanceof PluginKey),\n ) as WidgetSpec[];\n\n return {widgetSpecs, hasFocus: globalState.hasFocus};\n}\n\nfunction initState(state: EditorState): PlaceholderPluginState {\n const pluginKeys = getPlaceholderPluginKeys(state.schema);\n const {widgetSpecs, hasFocus} = getPlaceholderWidgetSpecs(state, pluginKeys);\n const decorationSet = DecorationSet.create(\n state.doc,\n widgetSpecs.map((widget) => Decoration.widget(widget.pos, widget.toDOM, widget.spec)),\n );\n\n return {decorationSet, hasFocus, pluginKeys};\n}\n\nfunction applyState(\n tr: Transaction,\n oldPluginState: PlaceholderPluginState,\n oldState: EditorState,\n newState: EditorState,\n): PlaceholderPluginState {\n // Early return if document hasn't changed and selection hasn't changed\n // This avoids unnecessary recalculation of decorations\n if (!tr.docChanged && !tr.selectionSet) {\n return oldPluginState;\n }\n\n // Reuse cached plugin keys if schema hasn't changed\n const pluginKeys =\n oldState.schema === newState.schema\n ? oldPluginState.pluginKeys\n : getPlaceholderPluginKeys(newState.schema);\n\n const {widgetSpecs, hasFocus} = getPlaceholderWidgetSpecs(newState, pluginKeys);\n const {decorationSet} = oldPluginState;\n const oldMappedSet = decorationSet.map(tr.mapping, tr.doc);\n\n // Find all decorations that are present in old and new set\n const unchangedPositions = new Set<number>();\n const decorationsThatDidNotChange: Decoration[] = [];\n\n widgetSpecs.forEach(({pos, spec}) => {\n const deco = oldMappedSet.find(pos, pos);\n if (deco.length && isEqual(deco[0].spec, spec)) {\n unchangedPositions.add(pos);\n decorationsThatDidNotChange.push(...deco);\n }\n });\n\n // Those are decorations that are present only in new set\n const newAddedDecorations = widgetSpecs.filter(({pos}) => !unchangedPositions.has(pos));\n\n // That is a set with decorations that are present in old set and absent in new set\n const notRelevantDecorations = oldMappedSet.remove(decorationsThatDidNotChange);\n let newSet = oldMappedSet;\n // Remove decorations that are not present in new set\n if (notRelevantDecorations.find().length) newSet = newSet.remove(notRelevantDecorations.find());\n // Add new decorations\n if (newAddedDecorations.length)\n newSet = newSet.add(\n tr.doc,\n newAddedDecorations.map((widget) =>\n Decoration.widget(widget.pos, widget.toDOM, widget.spec),\n ),\n );\n\n return {decorationSet: newSet, hasFocus, pluginKeys};\n}\n\ndeclare module 'prosemirror-model' {\n interface NodeSpec {\n placeholder?: {\n content: string | ((node: Node, parent?: Node | null) => string | null);\n customPlugin?: PluginKey<DecorationSet>;\n alwaysVisible?: boolean;\n };\n }\n}\n\ndeclare global {\n namespace WysiwygEditor {\n interface Context {\n placeholder: PlaceholderOptions;\n }\n }\n}\n"]}
|
|
@@ -2,4 +2,5 @@ import { type NodeType } from 'prosemirror-model';
|
|
|
2
2
|
import type { Command } from 'prosemirror-state';
|
|
3
3
|
export declare function toList(listType: NodeType): Command;
|
|
4
4
|
export declare const joinPrevList: Command;
|
|
5
|
+
export declare function liftEmptyListItem(itemType: NodeType): Command;
|
|
5
6
|
export declare function sinkOnlySelectedListItem(itemType: NodeType): Command;
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
import { liftEmptyBlock } from 'prosemirror-commands';
|
|
1
2
|
import { Fragment, Slice } from 'prosemirror-model';
|
|
2
3
|
import { wrapInList } from 'prosemirror-schema-list';
|
|
3
4
|
import { ReplaceAroundStep, liftTarget } from 'prosemirror-transform';
|
|
4
5
|
import { joinPreviousBlock } from "../../../commands/join.js";
|
|
6
|
+
import { get$CursorAtBlockStart } from "../../../utils/selection.js";
|
|
5
7
|
import { findAnyParentList, isListNode, isListOrItemNode } from "./utils.js";
|
|
6
8
|
export function toList(listType) {
|
|
7
9
|
return (state, dispatch) => {
|
|
@@ -19,6 +21,17 @@ export const joinPrevList = joinPreviousBlock({
|
|
|
19
21
|
checkPrevNode: isListNode,
|
|
20
22
|
skipNode: isListOrItemNode,
|
|
21
23
|
});
|
|
24
|
+
export function liftEmptyListItem(itemType) {
|
|
25
|
+
return (state, dispatch) => {
|
|
26
|
+
const $cursor = get$CursorAtBlockStart(state.selection);
|
|
27
|
+
if (!$cursor ||
|
|
28
|
+
$cursor.parent.content.size !== 0 ||
|
|
29
|
+
$cursor.node(-1).type !== itemType ||
|
|
30
|
+
$cursor.node(-1).childCount !== 1)
|
|
31
|
+
return false;
|
|
32
|
+
return liftEmptyBlock(state, dispatch);
|
|
33
|
+
};
|
|
34
|
+
}
|
|
22
35
|
/*
|
|
23
36
|
Simplified `sinkListItem` from `prosemirror-schema-list` without `state`/`dispatch`,
|
|
24
37
|
sinks list items deeper.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"commands.js","sourceRoot":"../../../../../src","sources":["extensions/markdown/Lists/commands.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,QAAQ,EAAiC,KAAK,EAAC,MAAM,mBAAmB,CAAC;AACjF,OAAO,EAAC,UAAU,EAAC,MAAM,yBAAyB,CAAC;AAEnD,OAAO,EAAC,iBAAiB,EAAE,UAAU,EAAC,MAAM,uBAAuB,CAAC;AAEpE,OAAO,EAAC,iBAAiB,EAAC,kCAA+B;
|
|
1
|
+
{"version":3,"file":"commands.js","sourceRoot":"../../../../../src","sources":["extensions/markdown/Lists/commands.ts"],"names":[],"mappings":"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","sourcesContent":["import {liftEmptyBlock} from 'prosemirror-commands';\nimport {Fragment, type NodeRange, type NodeType, Slice} from 'prosemirror-model';\nimport {wrapInList} from 'prosemirror-schema-list';\nimport type {Command, Transaction} from 'prosemirror-state';\nimport {ReplaceAroundStep, liftTarget} from 'prosemirror-transform';\n\nimport {joinPreviousBlock} from '../../../commands/join';\nimport {get$CursorAtBlockStart} from '../../../utils/selection';\n\nimport {findAnyParentList, isListNode, isListOrItemNode} from './utils';\n\nexport function toList(listType: NodeType): Command {\n return (state, dispatch) => {\n const parentList = findAnyParentList(state.schema)(state.selection);\n if (parentList) {\n if (listType === parentList.node.type) return true;\n\n dispatch?.(state.tr.setNodeMarkup(parentList.pos, listType));\n return true;\n }\n return wrapInList(listType)(state, dispatch);\n };\n}\n\nexport const joinPrevList = joinPreviousBlock({\n checkPrevNode: isListNode,\n skipNode: isListOrItemNode,\n});\n\nexport function liftEmptyListItem(itemType: NodeType): Command {\n return (state, dispatch) => {\n const $cursor = get$CursorAtBlockStart(state.selection);\n if (\n !$cursor ||\n $cursor.parent.content.size !== 0 ||\n $cursor.node(-1).type !== itemType ||\n $cursor.node(-1).childCount !== 1\n )\n return false;\n\n return liftEmptyBlock(state, dispatch);\n };\n}\n\n/*\n Simplified `sinkListItem` from `prosemirror-schema-list` without `state`/`dispatch`,\n sinks list items deeper.\n */\nconst sink = (tr: Transaction, range: NodeRange, itemType: NodeType) => {\n const before = tr.mapping.map(range.start);\n const after = tr.mapping.map(range.end);\n const startIndex = tr.mapping.map(range.startIndex);\n\n const parent = range.parent;\n const nodeBefore = parent.child(startIndex - 1);\n\n const nestedBefore = nodeBefore.lastChild && nodeBefore.lastChild.type === parent.type;\n const inner = Fragment.from(nestedBefore ? itemType.create() : null);\n const slice = new Slice(\n Fragment.from(itemType.create(null, Fragment.from(parent.type.create(null, inner)))),\n nestedBefore ? 3 : 1,\n 0,\n );\n\n tr.step(\n new ReplaceAroundStep(\n before - (nestedBefore ? 3 : 1),\n after,\n before,\n after,\n slice,\n 1,\n true,\n ),\n );\n return true;\n};\n\nexport function sinkOnlySelectedListItem(itemType: NodeType): Command {\n return ({tr, selection}, dispatch) => {\n const {$from, $to} = selection;\n const selectionRange = $from.blockRange(\n $to,\n (node) => node.childCount > 0 && node.firstChild!.type === itemType,\n );\n if (!selectionRange) {\n return false;\n }\n\n const {startIndex, parent, start, end} = selectionRange;\n if (startIndex === 0) {\n return false;\n }\n\n const nodeBefore = parent.child(startIndex - 1);\n if (nodeBefore.type !== itemType) {\n return false;\n }\n\n if (dispatch) {\n // lifts following list items sequentially to prepare correct nesting structure\n let currentEnd = end - 1;\n while (currentEnd > start) {\n const selectionEnd = tr.mapping.map($to.pos);\n\n const $candidateBlockEnd = tr.doc.resolve(currentEnd);\n const candidateBlockStartPos = $candidateBlockEnd.before($candidateBlockEnd.depth);\n const $candidateBlockStart = tr.doc.resolve(candidateBlockStartPos);\n const candidateBlockRange = $candidateBlockStart.blockRange($candidateBlockEnd);\n\n if (candidateBlockRange?.start) {\n const $rangeStart = tr.doc.resolve(candidateBlockRange.start);\n const shouldLift =\n candidateBlockRange.start > selectionEnd && isListNode($rangeStart.parent);\n\n if (shouldLift) {\n currentEnd = candidateBlockRange.start;\n\n const targetDepth = liftTarget(candidateBlockRange);\n if (targetDepth !== null) {\n tr.lift(candidateBlockRange, targetDepth);\n }\n }\n }\n\n currentEnd--;\n }\n\n // sinks the selected list item deeper into the list hierarchy\n sink(tr, selectionRange, itemType);\n\n dispatch(tr.scrollIntoView());\n return true;\n }\n return true;\n };\n}\n"]}
|
|
@@ -2,7 +2,7 @@ import { liftListItem, splitListItem } from 'prosemirror-schema-list';
|
|
|
2
2
|
import { withLogAction } from "../../../utils/keymap.js";
|
|
3
3
|
import { ListsSpecs, blType, liType, olType } from "./ListsSpecs/index.js";
|
|
4
4
|
import { actions } from "./actions.js";
|
|
5
|
-
import { joinPrevList, sinkOnlySelectedListItem, toList } from "./commands.js";
|
|
5
|
+
import { joinPrevList, liftEmptyListItem, sinkOnlySelectedListItem, toList } from "./commands.js";
|
|
6
6
|
import { ListAction } from "./const.js";
|
|
7
7
|
import { ListsInputRulesExtension } from "./inputrules.js";
|
|
8
8
|
import { collapseListsPlugin } from "./plugins/CollapseListsPlugin.js";
|
|
@@ -20,6 +20,7 @@ export const Lists = (builder, opts) => {
|
|
|
20
20
|
return {
|
|
21
21
|
Tab: sinkOnlySelectedListItem(liType(schema)),
|
|
22
22
|
'Shift-Tab': liftListItem(liType(schema)),
|
|
23
|
+
Backspace: liftEmptyListItem(liType(schema)),
|
|
23
24
|
'Mod-[': liftListItem(liType(schema)),
|
|
24
25
|
'Mod-]': sinkOnlySelectedListItem(liType(schema)),
|
|
25
26
|
...bindings,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"../../../../../src","sources":["extensions/markdown/Lists/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,YAAY,EAAE,aAAa,EAAC,MAAM,yBAAyB,CAAC;AAGpE,OAAO,EAAC,aAAa,EAAC,iCAA8B;AAEpD,OAAO,EAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAC,8BAAqB;AAChE,OAAO,EAAC,OAAO,EAAC,qBAAkB;AAClC,OAAO,EAAC,YAAY,EAAE,wBAAwB,EAAE,MAAM,EAAC,sBAAmB;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"../../../../../src","sources":["extensions/markdown/Lists/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,YAAY,EAAE,aAAa,EAAC,MAAM,yBAAyB,CAAC;AAGpE,OAAO,EAAC,aAAa,EAAC,iCAA8B;AAEpD,OAAO,EAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAC,8BAAqB;AAChE,OAAO,EAAC,OAAO,EAAC,qBAAkB;AAClC,OAAO,EAAC,YAAY,EAAE,iBAAiB,EAAE,wBAAwB,EAAE,MAAM,EAAC,sBAAmB;AAC7F,OAAO,EAAC,UAAU,EAAC,mBAAgB;AACnC,OAAO,EAAC,wBAAwB,EAA8B,wBAAqB;AACnF,OAAO,EAAC,mBAAmB,EAAC,yCAAsC;AAClE,OAAO,EAAC,gBAAgB,EAAC,sCAAmC;AAE5D,OAAO,EAAC,QAAQ,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAC,8BAAqB;AAQzE,MAAM,CAAC,MAAM,KAAK,GAAgC,CAAC,OAAO,EAAE,IAAI,EAAE,EAAE;IAChE,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;IAExB,OAAO,CAAC,SAAS,CAAC,CAAC,EAAC,MAAM,EAAC,EAAE,EAAE;QAC3B,MAAM,EAAC,KAAK,EAAE,KAAK,EAAC,GAAG,IAAI,IAAI,EAAE,CAAC;QAClC,MAAM,QAAQ,GAAW,EAAE,CAAC;QAC5B,IAAI,KAAK;YAAE,QAAQ,CAAC,KAAK,CAAC,GAAG,aAAa,CAAC,YAAY,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QACjF,IAAI,KAAK;YAAE,QAAQ,CAAC,KAAK,CAAC,GAAG,aAAa,CAAC,aAAa,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QAElF,OAAO;YACH,GAAG,EAAE,wBAAwB,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;YAC7C,WAAW,EAAE,YAAY,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;YACzC,SAAS,EAAE,iBAAiB,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;YAE5C,OAAO,EAAE,YAAY,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;YACrC,OAAO,EAAE,wBAAwB,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;YAEjD,GAAG,QAAQ;SACd,CAAC;IACN,CAAC,EAAE,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;IAE1B,OAAO,CAAC,SAAS,CACb,CAAC,EAAC,MAAM,EAAC,EAAE,EAAE,CAAC,CAAC;QACX,KAAK,EAAE,aAAa,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QACpC,SAAS,EAAE,YAAY;KAC1B,CAAC,EACF,OAAO,CAAC,QAAQ,CAAC,GAAG,CACvB,CAAC;IAEF,OAAO,CAAC,GAAG,CAAC,wBAAwB,EAAE,EAAC,mBAAmB,EAAE,IAAI,EAAE,YAAY,EAAC,CAAC,CAAC;IAEjF,OAAO,CAAC,SAAS,CAAC,gBAAgB,CAAC,CAAC;IAEpC,OAAO,CAAC,SAAS,CAAC,mBAAmB,CAAC,CAAC;IAEvC,OAAO;SACF,SAAS,CAAC,UAAU,CAAC,YAAY,EAAE,OAAO,CAAC,YAAY,CAAC;SACxD,SAAS,CAAC,UAAU,CAAC,aAAa,EAAE,OAAO,CAAC,aAAa,CAAC;SAC1D,SAAS,CAAC,UAAU,CAAC,YAAY,EAAE,OAAO,CAAC,YAAY,CAAC;SACxD,SAAS,CAAC,UAAU,CAAC,YAAY,EAAE,OAAO,CAAC,YAAY,CAAC,CAAC;AAClE,CAAC,CAAC","sourcesContent":["import {liftListItem, splitListItem} from 'prosemirror-schema-list';\n\nimport type {Action, ExtensionAuto, Keymap} from '../../../core';\nimport {withLogAction} from '../../../utils/keymap';\n\nimport {ListsSpecs, blType, liType, olType} from './ListsSpecs';\nimport {actions} from './actions';\nimport {joinPrevList, liftEmptyListItem, sinkOnlySelectedListItem, toList} from './commands';\nimport {ListAction} from './const';\nimport {ListsInputRulesExtension, type ListsInputRulesOptions} from './inputrules';\nimport {collapseListsPlugin} from './plugins/CollapseListsPlugin';\nimport {mergeListsPlugin} from './plugins/MergeListsPlugin';\n\nexport {ListNode, ListsAttr, blType, liType, olType} from './ListsSpecs';\n\nexport type ListsOptions = {\n ulKey?: string | null;\n olKey?: string | null;\n ulInputRules?: ListsInputRulesOptions['bulletListInputRule'];\n};\n\nexport const Lists: ExtensionAuto<ListsOptions> = (builder, opts) => {\n builder.use(ListsSpecs);\n\n builder.addKeymap(({schema}) => {\n const {ulKey, olKey} = opts ?? {};\n const bindings: Keymap = {};\n if (ulKey) bindings[ulKey] = withLogAction('bulletList', toList(blType(schema)));\n if (olKey) bindings[olKey] = withLogAction('orderedList', toList(olType(schema)));\n\n return {\n Tab: sinkOnlySelectedListItem(liType(schema)),\n 'Shift-Tab': liftListItem(liType(schema)),\n Backspace: liftEmptyListItem(liType(schema)),\n\n 'Mod-[': liftListItem(liType(schema)),\n 'Mod-]': sinkOnlySelectedListItem(liType(schema)),\n\n ...bindings,\n };\n }, builder.Priority.High);\n\n builder.addKeymap(\n ({schema}) => ({\n Enter: splitListItem(liType(schema)),\n Backspace: joinPrevList,\n }),\n builder.Priority.Low,\n );\n\n builder.use(ListsInputRulesExtension, {bulletListInputRule: opts?.ulInputRules});\n\n builder.addPlugin(mergeListsPlugin);\n\n builder.addPlugin(collapseListsPlugin);\n\n builder\n .addAction(ListAction.ToBulletList, actions.toBulletList)\n .addAction(ListAction.ToOrderedList, actions.toOrderedList)\n .addAction(ListAction.SinkListItem, actions.sinkListItem)\n .addAction(ListAction.LiftListItem, actions.liftListItem);\n};\n\ndeclare global {\n namespace WysiwygEditor {\n interface Actions {\n [ListAction.ToBulletList]: Action;\n [ListAction.ToOrderedList]: Action;\n [ListAction.SinkListItem]: Action;\n [ListAction.LiftListItem]: Action;\n }\n }\n}\n"]}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
type AutoScrollDirection = 'horizontal' | 'vertical' | 'both';
|
|
2
|
+
type AutoScrollOptions = {
|
|
3
|
+
edgeThreshold?: number;
|
|
4
|
+
maxSpeed?: number;
|
|
5
|
+
};
|
|
6
|
+
export declare class DnDAutoScroller {
|
|
7
|
+
private readonly _container;
|
|
8
|
+
private readonly _direction;
|
|
9
|
+
private readonly _edgeThreshold;
|
|
10
|
+
private readonly _maxSpeed;
|
|
11
|
+
private _clientX;
|
|
12
|
+
private _clientY;
|
|
13
|
+
private _rafId;
|
|
14
|
+
private _active;
|
|
15
|
+
constructor(scrollContainer: HTMLElement, direction: AutoScrollDirection, options?: AutoScrollOptions);
|
|
16
|
+
update(clientX: number, clientY: number): void;
|
|
17
|
+
destroy(): void;
|
|
18
|
+
private _startLoop;
|
|
19
|
+
private _getVisibleRect;
|
|
20
|
+
private _scrollStep;
|
|
21
|
+
/**
|
|
22
|
+
* Returns scroll delta for one axis.
|
|
23
|
+
* Negative when cursor is near the start edge, positive when near the end edge.
|
|
24
|
+
*/
|
|
25
|
+
private _calcDelta;
|
|
26
|
+
}
|
|
27
|
+
export {};
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
const DEFAULT_EDGE_THRESHOLD = 40; // px
|
|
2
|
+
const DEFAULT_MAX_SPEED = 15; // px per frame
|
|
3
|
+
export class DnDAutoScroller {
|
|
4
|
+
_container;
|
|
5
|
+
_direction;
|
|
6
|
+
_edgeThreshold;
|
|
7
|
+
_maxSpeed;
|
|
8
|
+
_clientX = 0;
|
|
9
|
+
_clientY = 0;
|
|
10
|
+
_rafId = null;
|
|
11
|
+
_active = false;
|
|
12
|
+
constructor(scrollContainer, direction, options) {
|
|
13
|
+
this._container = scrollContainer;
|
|
14
|
+
this._direction = direction;
|
|
15
|
+
this._edgeThreshold = options?.edgeThreshold ?? DEFAULT_EDGE_THRESHOLD;
|
|
16
|
+
this._maxSpeed = options?.maxSpeed ?? DEFAULT_MAX_SPEED;
|
|
17
|
+
}
|
|
18
|
+
update(clientX, clientY) {
|
|
19
|
+
this._clientX = clientX;
|
|
20
|
+
this._clientY = clientY;
|
|
21
|
+
if (!this._active) {
|
|
22
|
+
this._active = true;
|
|
23
|
+
this._startLoop();
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
destroy() {
|
|
27
|
+
if (this._rafId !== null) {
|
|
28
|
+
cancelAnimationFrame(this._rafId);
|
|
29
|
+
this._rafId = null;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
_startLoop() {
|
|
33
|
+
const tick = () => {
|
|
34
|
+
this._scrollStep();
|
|
35
|
+
this._rafId = requestAnimationFrame(tick);
|
|
36
|
+
};
|
|
37
|
+
this._rafId = requestAnimationFrame(tick);
|
|
38
|
+
}
|
|
39
|
+
_getVisibleRect() {
|
|
40
|
+
if (this._container === this._container.ownerDocument.documentElement) {
|
|
41
|
+
return { left: 0, top: 0, right: window.innerWidth, bottom: window.innerHeight };
|
|
42
|
+
}
|
|
43
|
+
return this._container.getBoundingClientRect();
|
|
44
|
+
}
|
|
45
|
+
_scrollStep() {
|
|
46
|
+
const rect = this._getVisibleRect();
|
|
47
|
+
let dx = 0;
|
|
48
|
+
let dy = 0;
|
|
49
|
+
if (this._direction === 'horizontal' || this._direction === 'both') {
|
|
50
|
+
dx = this._calcDelta(this._clientX, rect.left, rect.right);
|
|
51
|
+
}
|
|
52
|
+
if (this._direction === 'vertical' || this._direction === 'both') {
|
|
53
|
+
dy = this._calcDelta(this._clientY, rect.top, rect.bottom);
|
|
54
|
+
}
|
|
55
|
+
if (dx !== 0 || dy !== 0) {
|
|
56
|
+
this._container.scrollBy(dx, dy);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Returns scroll delta for one axis.
|
|
61
|
+
* Negative when cursor is near the start edge, positive when near the end edge.
|
|
62
|
+
*/
|
|
63
|
+
_calcDelta(cursor, edgeStart, edgeEnd) {
|
|
64
|
+
const distToStart = cursor - edgeStart;
|
|
65
|
+
const distToEnd = edgeEnd - cursor;
|
|
66
|
+
if (distToStart >= 0 && distToStart < this._edgeThreshold) {
|
|
67
|
+
const ratio = 1 - distToStart / this._edgeThreshold;
|
|
68
|
+
return -Math.round(this._maxSpeed * ratio);
|
|
69
|
+
}
|
|
70
|
+
if (distToEnd >= 0 && distToEnd < this._edgeThreshold) {
|
|
71
|
+
const ratio = 1 - distToEnd / this._edgeThreshold;
|
|
72
|
+
return Math.round(this._maxSpeed * ratio);
|
|
73
|
+
}
|
|
74
|
+
return 0;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=dnd-auto-scroll.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dnd-auto-scroll.js","sourceRoot":"../../../../../../../../src","sources":["extensions/yfm/YfmTable/plugins/YfmTableControls/dnd/dnd-auto-scroll.ts"],"names":[],"mappings":"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","sourcesContent":["type AutoScrollDirection = 'horizontal' | 'vertical' | 'both';\n\ntype AutoScrollOptions = {\n edgeThreshold?: number;\n maxSpeed?: number;\n};\n\nconst DEFAULT_EDGE_THRESHOLD = 40; // px\nconst DEFAULT_MAX_SPEED = 15; // px per frame\n\nexport class DnDAutoScroller {\n private readonly _container: HTMLElement;\n private readonly _direction: AutoScrollDirection;\n private readonly _edgeThreshold: number;\n private readonly _maxSpeed: number;\n\n private _clientX = 0;\n private _clientY = 0;\n private _rafId: number | null = null;\n private _active = false;\n\n constructor(\n scrollContainer: HTMLElement,\n direction: AutoScrollDirection,\n options?: AutoScrollOptions,\n ) {\n this._container = scrollContainer;\n this._direction = direction;\n this._edgeThreshold = options?.edgeThreshold ?? DEFAULT_EDGE_THRESHOLD;\n this._maxSpeed = options?.maxSpeed ?? DEFAULT_MAX_SPEED;\n }\n\n update(clientX: number, clientY: number): void {\n this._clientX = clientX;\n this._clientY = clientY;\n\n if (!this._active) {\n this._active = true;\n this._startLoop();\n }\n }\n\n destroy(): void {\n if (this._rafId !== null) {\n cancelAnimationFrame(this._rafId);\n this._rafId = null;\n }\n }\n\n private _startLoop(): void {\n const tick = () => {\n this._scrollStep();\n this._rafId = requestAnimationFrame(tick);\n };\n this._rafId = requestAnimationFrame(tick);\n }\n\n private _getVisibleRect(): {left: number; right: number; top: number; bottom: number} {\n if (this._container === this._container.ownerDocument.documentElement) {\n return {left: 0, top: 0, right: window.innerWidth, bottom: window.innerHeight};\n }\n return this._container.getBoundingClientRect();\n }\n\n private _scrollStep(): void {\n const rect = this._getVisibleRect();\n let dx = 0;\n let dy = 0;\n\n if (this._direction === 'horizontal' || this._direction === 'both') {\n dx = this._calcDelta(this._clientX, rect.left, rect.right);\n }\n\n if (this._direction === 'vertical' || this._direction === 'both') {\n dy = this._calcDelta(this._clientY, rect.top, rect.bottom);\n }\n\n if (dx !== 0 || dy !== 0) {\n this._container.scrollBy(dx, dy);\n }\n }\n\n /**\n * Returns scroll delta for one axis.\n * Negative when cursor is near the start edge, positive when near the end edge.\n */\n private _calcDelta(cursor: number, edgeStart: number, edgeEnd: number): number {\n const distToStart = cursor - edgeStart;\n const distToEnd = edgeEnd - cursor;\n\n if (distToStart >= 0 && distToStart < this._edgeThreshold) {\n const ratio = 1 - distToStart / this._edgeThreshold;\n return -Math.round(this._maxSpeed * ratio);\n }\n\n if (distToEnd >= 0 && distToEnd < this._edgeThreshold) {\n const ratio = 1 - distToEnd / this._edgeThreshold;\n return Math.round(this._maxSpeed * ratio);\n }\n\n return 0;\n }\n}\n"]}
|
|
@@ -8,6 +8,7 @@ import { YfmTableNode } from "../../../YfmTableSpecs/index.js";
|
|
|
8
8
|
import { clearAllSelections, selectDraggedColumn, selectDraggedRow } from "../plugins/dnd-plugin.js";
|
|
9
9
|
import { hideHoverDecos } from "../plugins/focus-plugin.js";
|
|
10
10
|
import { getSelectedCellsForColumns, getSelectedCellsForRows } from "../utils.js";
|
|
11
|
+
import { DnDAutoScroller } from "./dnd-auto-scroll.js";
|
|
11
12
|
import { TableColumnDropCursor, TableRowDropCursor, } from "./dnd-drop-cursor.js";
|
|
12
13
|
import { YfmTableDnDGhost } from "./dnd-ghost.js";
|
|
13
14
|
import "./dnd.css";
|
|
@@ -162,6 +163,10 @@ class YfmTableRowDnDHandler extends YfmTableDnDAbstractHandler {
|
|
|
162
163
|
rangeIdx: draggedRangeIdx,
|
|
163
164
|
tableDesc,
|
|
164
165
|
});
|
|
166
|
+
const scrollContainer = findScrollableAncestor(this._editorView.dom, 'vertical');
|
|
167
|
+
const autoScroller = scrollContainer
|
|
168
|
+
? new DnDAutoScroller(scrollContainer, 'vertical')
|
|
169
|
+
: null;
|
|
165
170
|
const onMoveDebounced = debounce((event) => {
|
|
166
171
|
this._moveDragging(event, {
|
|
167
172
|
rangeIdx: draggedRangeIdx,
|
|
@@ -170,11 +175,13 @@ class YfmTableRowDnDHandler extends YfmTableDnDAbstractHandler {
|
|
|
170
175
|
}, MOUSE_MOVE_DEBOUNCE, { maxWait: MOUSE_MOVE_DEBOUNCE });
|
|
171
176
|
const onMove = (event) => {
|
|
172
177
|
ghost.move(event);
|
|
178
|
+
autoScroller?.update(event.clientX, event.clientY);
|
|
173
179
|
onMoveDebounced(event);
|
|
174
180
|
};
|
|
175
181
|
document.addEventListener('mousemove', onMove);
|
|
176
182
|
document.addEventListener('mouseup', () => {
|
|
177
183
|
onMoveDebounced.flush();
|
|
184
|
+
autoScroller?.destroy();
|
|
178
185
|
ghost.destroy();
|
|
179
186
|
document.removeEventListener('mousemove', onMove);
|
|
180
187
|
this._endDragging(currRowRange, tableDesc);
|
|
@@ -323,6 +330,8 @@ class YfmTableColumnDnDHandler extends YfmTableDnDAbstractHandler {
|
|
|
323
330
|
rangeIdx: draggedRangeIdx,
|
|
324
331
|
tableDesc,
|
|
325
332
|
});
|
|
333
|
+
const { node: tableElem } = this._editorView.domAtPos(tableDesc.pos + 1);
|
|
334
|
+
const autoScroller = tableElem instanceof HTMLElement ? new DnDAutoScroller(tableElem, 'horizontal') : null;
|
|
326
335
|
const onMoveDebounced = debounce((event) => {
|
|
327
336
|
this._moveDragging(event, {
|
|
328
337
|
rangeIdx: draggedRangeIdx,
|
|
@@ -331,11 +340,13 @@ class YfmTableColumnDnDHandler extends YfmTableDnDAbstractHandler {
|
|
|
331
340
|
}, MOUSE_MOVE_DEBOUNCE, { maxWait: MOUSE_MOVE_DEBOUNCE });
|
|
332
341
|
const onMove = (event) => {
|
|
333
342
|
ghost.move(event);
|
|
343
|
+
autoScroller?.update(event.clientX, event.clientY);
|
|
334
344
|
onMoveDebounced(event);
|
|
335
345
|
};
|
|
336
346
|
document.addEventListener('mousemove', onMove);
|
|
337
347
|
document.addEventListener('mouseup', () => {
|
|
338
348
|
onMoveDebounced.flush();
|
|
349
|
+
autoScroller?.destroy();
|
|
339
350
|
ghost.destroy();
|
|
340
351
|
document.removeEventListener('mousemove', onMove);
|
|
341
352
|
this._endDragging(currColumnRange, tableDesc);
|
|
@@ -475,4 +486,26 @@ function isDragThresholdPassed(init, curr) {
|
|
|
475
486
|
return (Math.abs(init.pageX - curr.pageX) >= DRAG_START_THRESHOLD ||
|
|
476
487
|
Math.abs(init.pageY - curr.pageY) >= DRAG_START_THRESHOLD);
|
|
477
488
|
}
|
|
489
|
+
function findScrollableAncestor(element, axis) {
|
|
490
|
+
const prop = axis === 'vertical' ? 'overflowY' : 'overflowX';
|
|
491
|
+
let current = element.parentElement;
|
|
492
|
+
while (current && current !== document.documentElement) {
|
|
493
|
+
const overflow = getComputedStyle(current)[prop];
|
|
494
|
+
if (overflow === 'auto' || overflow === 'scroll') {
|
|
495
|
+
const hasScroll = axis === 'vertical'
|
|
496
|
+
? current.scrollHeight > current.clientHeight
|
|
497
|
+
: current.scrollWidth > current.clientWidth;
|
|
498
|
+
if (hasScroll)
|
|
499
|
+
return current;
|
|
500
|
+
}
|
|
501
|
+
current = current.parentElement;
|
|
502
|
+
}
|
|
503
|
+
const docEl = element.ownerDocument.documentElement;
|
|
504
|
+
const hasDocScroll = axis === 'vertical'
|
|
505
|
+
? docEl.scrollHeight > docEl.clientHeight
|
|
506
|
+
: docEl.scrollWidth > docEl.clientWidth;
|
|
507
|
+
if (hasDocScroll)
|
|
508
|
+
return docEl;
|
|
509
|
+
return null;
|
|
510
|
+
}
|
|
478
511
|
//# sourceMappingURL=dnd.js.map
|