@lobehub/editor 4.0.2 → 4.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/es/plugins/code/plugin/index.js +6 -0
- package/es/plugins/markdown/service/shortcut.d.ts +1 -1
- package/es/plugins/markdown/service/transformers.d.ts +1 -1
- package/es/plugins/markdown/service/transformers.js +14 -2
- package/es/plugins/slash/react/ReactSlashPlugin.js +13 -2
- package/es/plugins/slash/react/components/DefaultSlashMenu.js +106 -21
- package/es/plugins/slash/react/components/SlashMenu.js +4 -0
- package/es/plugins/slash/react/type.d.ts +10 -0
- package/es/react/Editor/Editor.js +5 -1
- package/es/react/Editor/type.d.ts +4 -0
- package/es/react/FloatMenu/FloatMenu.js +4 -1
- package/es/react/FloatMenu/style.d.ts +2 -1
- package/es/react/FloatMenu/style.js +3 -2
- package/es/react/FloatMenu/type.d.ts +2 -0
- package/package.json +1 -1
|
@@ -82,6 +82,12 @@ export var CodePlugin = (_class = /*#__PURE__*/function (_KernelPlugin) {
|
|
|
82
82
|
});
|
|
83
83
|
markdownService.registerMarkdownShortCuts([{
|
|
84
84
|
process: function process(selection) {
|
|
85
|
+
// If selection already contains a code node, do not process the shortcut to avoid nesting code nodes
|
|
86
|
+
if (selection.getNodes().some(function (node) {
|
|
87
|
+
return node.getType() === CodeNode.getType();
|
|
88
|
+
})) {
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
85
91
|
var text = selection.getTextContent();
|
|
86
92
|
selection.removeText();
|
|
87
93
|
selection.insertNodes([$createCodeNode(text), $createCursorNode()]);
|
|
@@ -75,7 +75,7 @@ export declare class MarkdownShortCutService implements IMarkdownShortCutService
|
|
|
75
75
|
get textFormatTransformersByTrigger(): Readonly<Record<string, readonly Readonly<{
|
|
76
76
|
format?: readonly import("lexical").TextFormatType[] | undefined;
|
|
77
77
|
intraword?: boolean | undefined;
|
|
78
|
-
process?: ((selection: import("lexical").RangeSelection) => void) | undefined;
|
|
78
|
+
process?: ((selection: import("lexical").RangeSelection) => boolean | void) | undefined;
|
|
79
79
|
tag: string;
|
|
80
80
|
type: "text-format";
|
|
81
81
|
}>[]>>;
|
|
@@ -2,7 +2,7 @@ import type { ElementNode, LexicalNode, RangeSelection, TextFormatType, TextNode
|
|
|
2
2
|
export type TextFormatTransformer = Readonly<{
|
|
3
3
|
format?: ReadonlyArray<TextFormatType>;
|
|
4
4
|
intraword?: boolean;
|
|
5
|
-
process?: (selection: RangeSelection) => void;
|
|
5
|
+
process?: (selection: RangeSelection) => boolean | void;
|
|
6
6
|
tag: string;
|
|
7
7
|
type: 'text-format';
|
|
8
8
|
}>;
|
|
@@ -9,7 +9,7 @@ function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
|
|
|
9
9
|
function _createForOfIteratorHelper(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it.return != null) it.return(); } finally { if (didErr) throw err; } } }; }
|
|
10
10
|
function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
|
|
11
11
|
function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; }
|
|
12
|
-
import { $createRangeSelection, $getSelection, $isLineBreakNode, $isRangeSelection, $isRootOrShadowRoot, $isTextNode, $setSelection } from 'lexical';
|
|
12
|
+
import { $createRangeSelection, $getNodeByKey, $getSelection, $isLineBreakNode, $isRangeSelection, $isRootOrShadowRoot, $isTextNode, $setSelection } from 'lexical';
|
|
13
13
|
import { PUNCTUATION_OR_SPACE, getOpenTagStartIndex, isEqualSubString } from "../utils";
|
|
14
14
|
export function testElementTransformers(parentNode, anchorNode, anchorOffset, elementTransformers, fromTrigger) {
|
|
15
15
|
var grandParentNode = parentNode.getParent();
|
|
@@ -230,7 +230,19 @@ export function $runTextFormatTransformers(anchorNode, anchorOffset, textFormatT
|
|
|
230
230
|
nextSelection.anchor.set(openNode.__key, openTagStartIndex, 'text');
|
|
231
231
|
nextSelection.focus.set(closeNode.__key, newOffset, 'text');
|
|
232
232
|
if (matcher.process) {
|
|
233
|
-
matcher.process(nextSelection)
|
|
233
|
+
if (matcher.process(nextSelection) === false) {
|
|
234
|
+
var currentOpenNode = $getNodeByKey(openNode.__key);
|
|
235
|
+
var currentCloseNode = $getNodeByKey(closeNode.__key);
|
|
236
|
+
if ($isTextNode(currentOpenNode)) {
|
|
237
|
+
currentOpenNode.setTextContent(prevOpenNodeText);
|
|
238
|
+
}
|
|
239
|
+
if (currentCloseNode !== currentOpenNode && $isTextNode(currentCloseNode)) {
|
|
240
|
+
currentCloseNode.setTextContent(prevCloseNodeText);
|
|
241
|
+
}
|
|
242
|
+
// If process function returns false, cancel the transform and set selection to original position
|
|
243
|
+
$setSelection(anchorNode.selectEnd());
|
|
244
|
+
continue;
|
|
245
|
+
}
|
|
234
246
|
return true;
|
|
235
247
|
} else if (matcher.format) {
|
|
236
248
|
// Apply formatting to selected text
|
|
@@ -8,7 +8,7 @@ function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" !=
|
|
|
8
8
|
function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
|
|
9
9
|
import { mergeRegister } from '@lexical/utils';
|
|
10
10
|
import { COMMAND_PRIORITY_CRITICAL, KEY_ARROW_DOWN_COMMAND, KEY_ARROW_UP_COMMAND, KEY_DOWN_COMMAND, KEY_ESCAPE_COMMAND, KEY_TAB_COMMAND } from 'lexical';
|
|
11
|
-
import { Children, useCallback, useLayoutEffect, useRef, useState } from 'react';
|
|
11
|
+
import { Children, useCallback, useEffect, useLayoutEffect, useRef, useState } from 'react';
|
|
12
12
|
import { noop } from "../../../editor-kernel";
|
|
13
13
|
import { useLexicalEditor } from "../../../editor-kernel/react";
|
|
14
14
|
import { useLexicalComposerContext } from "../../../editor-kernel/react/react-context";
|
|
@@ -20,7 +20,9 @@ import { setCancelablePromise } from "./utils";
|
|
|
20
20
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
21
21
|
var ReactSlashPlugin = function ReactSlashPlugin(_ref) {
|
|
22
22
|
var children = _ref.children,
|
|
23
|
-
anchorClassName = _ref.anchorClassName
|
|
23
|
+
anchorClassName = _ref.anchorClassName,
|
|
24
|
+
getPopupContainer = _ref.getPopupContainer,
|
|
25
|
+
placement = _ref.placement;
|
|
24
26
|
var _useLexicalComposerCo = useLexicalComposerContext(),
|
|
25
27
|
_useLexicalComposerCo2 = _slicedToArray(_useLexicalComposerCo, 1),
|
|
26
28
|
editor = _useLexicalComposerCo2[0];
|
|
@@ -61,6 +63,11 @@ var ReactSlashPlugin = function ReactSlashPlugin(_ref) {
|
|
|
61
63
|
setResolution(null);
|
|
62
64
|
setActiveKey(null);
|
|
63
65
|
}, []);
|
|
66
|
+
|
|
67
|
+
// Close menu on unmount to prevent orphaned portal at (0,0)
|
|
68
|
+
useEffect(function () {
|
|
69
|
+
return close;
|
|
70
|
+
}, [close]);
|
|
64
71
|
var handleActiveKeyChange = useCallback(function (key) {
|
|
65
72
|
setActiveKey(key);
|
|
66
73
|
}, []);
|
|
@@ -113,6 +120,8 @@ var ReactSlashPlugin = function ReactSlashPlugin(_ref) {
|
|
|
113
120
|
}
|
|
114
121
|
var rect = ctx.getRect();
|
|
115
122
|
setDropdownPosition({
|
|
123
|
+
getRect: ctx.getRect,
|
|
124
|
+
rect: rect,
|
|
116
125
|
x: rect.left,
|
|
117
126
|
y: rect.bottom
|
|
118
127
|
});
|
|
@@ -234,12 +243,14 @@ var ReactSlashPlugin = function ReactSlashPlugin(_ref) {
|
|
|
234
243
|
activeKey: activeKey,
|
|
235
244
|
anchorClassName: anchorClassName,
|
|
236
245
|
customRender: CustomRender,
|
|
246
|
+
getPopupContainer: getPopupContainer,
|
|
237
247
|
loading: loading,
|
|
238
248
|
onActiveKeyChange: handleActiveKeyChange,
|
|
239
249
|
onClose: close,
|
|
240
250
|
onSelect: handleMenuSelect,
|
|
241
251
|
open: isOpen,
|
|
242
252
|
options: options,
|
|
253
|
+
placement: placement,
|
|
243
254
|
position: dropdownPosition
|
|
244
255
|
});
|
|
245
256
|
};
|
|
@@ -1,26 +1,107 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
var _templateObject;
|
|
3
|
+
var _templateObject, _templateObject2;
|
|
4
|
+
function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); }
|
|
5
|
+
function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
|
|
6
|
+
function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
|
|
7
|
+
function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); }
|
|
8
|
+
function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); }
|
|
9
|
+
function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; }
|
|
4
10
|
function _taggedTemplateLiteral(strings, raw) { if (!raw) { raw = strings.slice(0); } return Object.freeze(Object.defineProperties(strings, { raw: { value: Object.freeze(raw) } })); }
|
|
5
|
-
import {
|
|
11
|
+
import { flip, offset, shift, useFloating } from '@floating-ui/react';
|
|
12
|
+
import { Menu } from '@lobehub/ui';
|
|
6
13
|
import { createStaticStyles } from 'antd-style';
|
|
7
|
-
import { useCallback } from 'react';
|
|
14
|
+
import { useCallback, useEffect, useLayoutEffect, useMemo, useRef } from 'react';
|
|
15
|
+
import { createPortal } from 'react-dom';
|
|
8
16
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
17
|
+
var LOBE_THEME_APP_ID = 'lobe-ui-theme-app';
|
|
9
18
|
var styles = createStaticStyles(function (_ref) {
|
|
10
|
-
var css = _ref.css
|
|
19
|
+
var css = _ref.css,
|
|
20
|
+
cssVar = _ref.cssVar;
|
|
11
21
|
return {
|
|
12
|
-
menu: css(_templateObject || (_templateObject = _taggedTemplateLiteral(["\n
|
|
22
|
+
menu: css(_templateObject || (_templateObject = _taggedTemplateLiteral(["\n z-index: 9999;\n width: max-content;\n "]))),
|
|
23
|
+
popup: css(_templateObject2 || (_templateObject2 = _taggedTemplateLiteral(["\n scrollbar-width: none;\n\n overflow-y: auto;\n\n min-width: 120px;\n max-height: min(50vh, 400px);\n padding: 4px;\n border-radius: ", ";\n\n background: ", ";\n outline: none;\n box-shadow:\n 0 0 15px 0 #00000008,\n 0 2px 30px 0 #00000014,\n 0 0 0 1px ", " inset;\n\n .ant-menu {\n min-width: 200px;\n padding: 0 !important;\n border-inline-end: none !important;\n\n color: ", " !important;\n\n background: transparent !important;\n background-color: transparent !important;\n }\n\n .ant-menu-item {\n overflow: hidden;\n display: flex !important;\n align-items: center;\n\n width: 100% !important;\n min-height: 36px;\n margin: 0 !important;\n padding-block: 8px !important;\n padding-inline: 12px !important;\n border-radius: ", " !important;\n\n font-size: 14px;\n line-height: 20px;\n color: ", " !important;\n\n background: transparent !important;\n\n transition: all 150ms ", ";\n\n &:hover,\n &.ant-menu-item-active {\n background: ", " !important;\n }\n\n &:active {\n background: ", " !important;\n }\n }\n\n .ant-menu-item-divider {\n height: 1px;\n margin-block: 4px !important;\n margin-inline: 0 !important;\n background: ", " !important;\n }\n\n .ant-menu-title-content,\n .ant-menu-title-content-with-extra {\n overflow: visible;\n display: inline-flex;\n gap: 24px;\n justify-content: space-between;\n\n width: 100%;\n\n text-overflow: unset;\n }\n "])), cssVar.borderRadius, cssVar.colorBgElevated, cssVar.colorBorder, cssVar.colorText, cssVar.borderRadiusSM, cssVar.colorText, cssVar.motionEaseOut, cssVar.colorFillTertiary, cssVar.colorFillSecondary, cssVar.colorBorder)
|
|
13
24
|
};
|
|
14
25
|
});
|
|
15
26
|
var DefaultSlashMenu = function DefaultSlashMenu(_ref2) {
|
|
16
27
|
var activeKey = _ref2.activeKey,
|
|
17
|
-
|
|
28
|
+
getPopupContainer = _ref2.getPopupContainer,
|
|
18
29
|
loading = _ref2.loading,
|
|
19
30
|
onSelect = _ref2.onSelect,
|
|
20
31
|
open = _ref2.open,
|
|
21
32
|
options = _ref2.options,
|
|
22
|
-
|
|
23
|
-
|
|
33
|
+
forcePlacement = _ref2.placement,
|
|
34
|
+
position = _ref2.position;
|
|
35
|
+
var resolvedPlacement = forcePlacement ? "".concat(forcePlacement, "-start") : 'top-start';
|
|
36
|
+
var middleware = useMemo(function () {
|
|
37
|
+
return [offset(8)].concat(_toConsumableArray(!forcePlacement ? [flip()] : []), [shift({
|
|
38
|
+
padding: 8
|
|
39
|
+
})]);
|
|
40
|
+
}, [forcePlacement]);
|
|
41
|
+
|
|
42
|
+
// Keep getRect in a ref so the virtual reference always calls the latest version
|
|
43
|
+
var getRectRef = useRef(position.getRect);
|
|
44
|
+
getRectRef.current = position.getRect;
|
|
45
|
+
var _useFloating = useFloating({
|
|
46
|
+
middleware: middleware,
|
|
47
|
+
open: open,
|
|
48
|
+
placement: resolvedPlacement,
|
|
49
|
+
strategy: 'fixed'
|
|
50
|
+
}),
|
|
51
|
+
refs = _useFloating.refs,
|
|
52
|
+
floatingStyles = _useFloating.floatingStyles,
|
|
53
|
+
update = _useFloating.update;
|
|
54
|
+
useLayoutEffect(function () {
|
|
55
|
+
if (!position.rect) return;
|
|
56
|
+
refs.setPositionReference({
|
|
57
|
+
getBoundingClientRect: function getBoundingClientRect() {
|
|
58
|
+
var _getRectRef$current, _getRectRef$current2;
|
|
59
|
+
return (_getRectRef$current = (_getRectRef$current2 = getRectRef.current) === null || _getRectRef$current2 === void 0 ? void 0 : _getRectRef$current2.call(getRectRef)) !== null && _getRectRef$current !== void 0 ? _getRectRef$current : position.rect;
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
}, [position.rect, refs]);
|
|
63
|
+
|
|
64
|
+
// Force position recalculation after reference is set.
|
|
65
|
+
// useFloating computes before useLayoutEffect sets the reference,
|
|
66
|
+
// so the first frame has wrong position. rAF ensures a correct update.
|
|
67
|
+
useEffect(function () {
|
|
68
|
+
if (!open || !position.rect) return;
|
|
69
|
+
var frame = requestAnimationFrame(function () {
|
|
70
|
+
return update();
|
|
71
|
+
});
|
|
72
|
+
return function () {
|
|
73
|
+
return cancelAnimationFrame(frame);
|
|
74
|
+
};
|
|
75
|
+
}, [open, position.rect, update]);
|
|
76
|
+
|
|
77
|
+
// Listen to scroll events to update floating position.
|
|
78
|
+
// capture phase on window catches scroll from any ancestor (scroll events don't bubble,
|
|
79
|
+
// but do propagate during capture). Also listen on getPopupContainer for edge cases
|
|
80
|
+
// like shadow DOM where capture on window may not reach.
|
|
81
|
+
useEffect(function () {
|
|
82
|
+
if (!open) return;
|
|
83
|
+
var onScroll = function onScroll() {
|
|
84
|
+
return update();
|
|
85
|
+
};
|
|
86
|
+
var container = getPopupContainer === null || getPopupContainer === void 0 ? void 0 : getPopupContainer();
|
|
87
|
+
window.addEventListener('scroll', onScroll, {
|
|
88
|
+
capture: true,
|
|
89
|
+
passive: true
|
|
90
|
+
});
|
|
91
|
+
if (container) {
|
|
92
|
+
container.addEventListener('scroll', onScroll, {
|
|
93
|
+
passive: true
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
return function () {
|
|
97
|
+
window.removeEventListener('scroll', onScroll, {
|
|
98
|
+
capture: true
|
|
99
|
+
});
|
|
100
|
+
if (container) {
|
|
101
|
+
container.removeEventListener('scroll', onScroll);
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
}, [open, getPopupContainer, update]);
|
|
24
105
|
var handleMenuClick = useCallback(function (_ref3) {
|
|
25
106
|
var key = _ref3.key;
|
|
26
107
|
var option = options.find(function (item) {
|
|
@@ -28,29 +109,33 @@ var DefaultSlashMenu = function DefaultSlashMenu(_ref2) {
|
|
|
28
109
|
});
|
|
29
110
|
if (option) onSelect(option);
|
|
30
111
|
}, [options, onSelect]);
|
|
31
|
-
|
|
112
|
+
var hasVisibleItems = options === null || options === void 0 ? void 0 : options.some(function (item) {
|
|
113
|
+
return !('type' in item && item.type === 'divider');
|
|
114
|
+
});
|
|
115
|
+
if (!open || !hasVisibleItems) return null;
|
|
116
|
+
var portalContainer = (getPopupContainer === null || getPopupContainer === void 0 ? void 0 : getPopupContainer()) || document.getElementById(LOBE_THEME_APP_ID) || document.body;
|
|
117
|
+
var node = /*#__PURE__*/_jsx("div", {
|
|
32
118
|
className: styles.menu,
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
119
|
+
"data-resloved-placement": resolvedPlacement,
|
|
120
|
+
ref: refs.setFloating,
|
|
121
|
+
style: floatingStyles,
|
|
122
|
+
children: /*#__PURE__*/_jsx("div", {
|
|
123
|
+
className: styles.popup,
|
|
124
|
+
children: /*#__PURE__*/_jsx(Menu
|
|
125
|
+
// @ts-ignore - activeKey is a valid antd Menu prop passed via ...rest
|
|
126
|
+
, {
|
|
40
127
|
activeKey: activeKey,
|
|
41
128
|
items: loading ? [{
|
|
42
129
|
disabled: true,
|
|
43
130
|
key: 'loading',
|
|
44
131
|
label: 'Loading...'
|
|
45
132
|
}] : options,
|
|
46
|
-
onClick: handleMenuClick
|
|
47
|
-
|
|
48
|
-
open: open,
|
|
49
|
-
children: /*#__PURE__*/_jsx("span", {
|
|
50
|
-
className: anchorClassName
|
|
133
|
+
onClick: handleMenuClick,
|
|
134
|
+
selectable: false
|
|
51
135
|
})
|
|
52
136
|
})
|
|
53
137
|
});
|
|
138
|
+
return /*#__PURE__*/createPortal(node, portalContainer);
|
|
54
139
|
};
|
|
55
140
|
DefaultSlashMenu.displayName = 'DefaultSlashMenu';
|
|
56
141
|
export default DefaultSlashMenu;
|
|
@@ -12,11 +12,13 @@ var SlashMenu = function SlashMenu(_ref) {
|
|
|
12
12
|
var activeKey = _ref.activeKey,
|
|
13
13
|
anchorClassName = _ref.anchorClassName,
|
|
14
14
|
CustomRender = _ref.customRender,
|
|
15
|
+
getPopupContainer = _ref.getPopupContainer,
|
|
15
16
|
loading = _ref.loading,
|
|
16
17
|
onActiveKeyChange = _ref.onActiveKeyChange,
|
|
17
18
|
onSelect = _ref.onSelect,
|
|
18
19
|
open = _ref.open,
|
|
19
20
|
options = _ref.options,
|
|
21
|
+
placement = _ref.placement,
|
|
20
22
|
position = _ref.position,
|
|
21
23
|
onClose = _ref.onClose;
|
|
22
24
|
// Adapter for custom render component onSelect
|
|
@@ -40,11 +42,13 @@ var SlashMenu = function SlashMenu(_ref) {
|
|
|
40
42
|
return /*#__PURE__*/_jsx(DefaultSlashMenu, {
|
|
41
43
|
activeKey: activeKey,
|
|
42
44
|
anchorClassName: anchorClassName,
|
|
45
|
+
getPopupContainer: getPopupContainer,
|
|
43
46
|
loading: loading,
|
|
44
47
|
onClose: onClose,
|
|
45
48
|
onSelect: onSelect,
|
|
46
49
|
open: open,
|
|
47
50
|
options: options,
|
|
51
|
+
placement: placement,
|
|
48
52
|
position: position
|
|
49
53
|
});
|
|
50
54
|
};
|
|
@@ -77,6 +77,8 @@ export interface SlashMenuProps {
|
|
|
77
77
|
anchorClassName?: string;
|
|
78
78
|
/** Custom render component if provided */
|
|
79
79
|
customRender?: FC<MenuRenderProps>;
|
|
80
|
+
/** Custom popup container for portal rendering and scroll tracking */
|
|
81
|
+
getPopupContainer?: () => HTMLElement | null;
|
|
80
82
|
/** Loading state */
|
|
81
83
|
loading: boolean;
|
|
82
84
|
/** Callback to set active key */
|
|
@@ -89,8 +91,12 @@ export interface SlashMenuProps {
|
|
|
89
91
|
open: boolean;
|
|
90
92
|
/** Available options to display */
|
|
91
93
|
options: Array<ISlashOption>;
|
|
94
|
+
/** Force menu placement direction, skipping auto-flip detection */
|
|
95
|
+
placement?: 'bottom' | 'top';
|
|
92
96
|
/** Menu position */
|
|
93
97
|
position: {
|
|
98
|
+
getRect?: () => DOMRect;
|
|
99
|
+
rect?: DOMRect;
|
|
94
100
|
x: number;
|
|
95
101
|
y: number;
|
|
96
102
|
};
|
|
@@ -98,4 +104,8 @@ export interface SlashMenuProps {
|
|
|
98
104
|
export interface ReactSlashPluginProps {
|
|
99
105
|
anchorClassName?: string;
|
|
100
106
|
children?: (ReactElement<ReactSlashOptionProps> | undefined) | (ReactElement<ReactSlashOptionProps> | undefined)[];
|
|
107
|
+
/** Custom popup container for portal rendering and scroll tracking */
|
|
108
|
+
getPopupContainer?: () => HTMLElement | null;
|
|
109
|
+
/** Force menu placement direction, skipping auto-flip detection */
|
|
110
|
+
placement?: 'bottom' | 'top';
|
|
101
111
|
}
|
|
@@ -42,6 +42,8 @@ var Editor = /*#__PURE__*/memo(function (_ref) {
|
|
|
42
42
|
plugins = _ref$plugins === void 0 ? [] : _ref$plugins,
|
|
43
43
|
_ref$slashOption = _ref.slashOption,
|
|
44
44
|
slashOption = _ref$slashOption === void 0 ? {} : _ref$slashOption,
|
|
45
|
+
slashPlacement = _ref.slashPlacement,
|
|
46
|
+
getPopupContainer = _ref.getPopupContainer,
|
|
45
47
|
_ref$mentionOption = _ref.mentionOption,
|
|
46
48
|
mentionOption = _ref$mentionOption === void 0 ? {} : _ref$mentionOption,
|
|
47
49
|
variant = _ref.variant,
|
|
@@ -100,6 +102,8 @@ var Editor = /*#__PURE__*/memo(function (_ref) {
|
|
|
100
102
|
var memoSlash = useMemo(function () {
|
|
101
103
|
if (!enableSlash && !enableMention) return null;
|
|
102
104
|
return /*#__PURE__*/_jsxs(ReactSlashPlugin, {
|
|
105
|
+
getPopupContainer: getPopupContainer,
|
|
106
|
+
placement: slashPlacement,
|
|
103
107
|
children: [enableSlash ? /*#__PURE__*/_jsx(ReactSlashOption, _objectSpread({
|
|
104
108
|
maxLength: 8,
|
|
105
109
|
trigger: "/"
|
|
@@ -108,7 +112,7 @@ var Editor = /*#__PURE__*/memo(function (_ref) {
|
|
|
108
112
|
trigger: "@"
|
|
109
113
|
}, restMentionOption)) : undefined]
|
|
110
114
|
});
|
|
111
|
-
}, [enableSlash, enableMention, slashOption, restMentionOption]);
|
|
115
|
+
}, [enableSlash, enableMention, slashOption, slashPlacement, getPopupContainer, restMentionOption]);
|
|
112
116
|
return /*#__PURE__*/_jsxs(ReactEditor, {
|
|
113
117
|
config: config,
|
|
114
118
|
editor: editor,
|
|
@@ -23,6 +23,8 @@ export interface EditorProps extends Partial<ReactEditorContentProps>, Omit<Reac
|
|
|
23
23
|
* @default true
|
|
24
24
|
*/
|
|
25
25
|
enablePasteMarkdown?: boolean;
|
|
26
|
+
/** Custom popup container for slash menu portal rendering and scroll tracking */
|
|
27
|
+
getPopupContainer?: () => HTMLElement | null;
|
|
26
28
|
markdownOption?: boolean | {
|
|
27
29
|
bold?: boolean;
|
|
28
30
|
code?: boolean;
|
|
@@ -42,6 +44,8 @@ export interface EditorProps extends Partial<ReactEditorContentProps>, Omit<Reac
|
|
|
42
44
|
onTextChange?: (editor: IEditor) => void;
|
|
43
45
|
plugins?: EditorPlugin[];
|
|
44
46
|
slashOption?: Partial<ReactSlashOptionProps>;
|
|
47
|
+
/** Force slash menu placement direction, skipping auto-flip detection */
|
|
48
|
+
slashPlacement?: 'bottom' | 'top';
|
|
45
49
|
style?: CSSProperties;
|
|
46
50
|
}
|
|
47
51
|
export {};
|
|
@@ -19,13 +19,16 @@ var FloatMenu = function FloatMenu(_ref) {
|
|
|
19
19
|
_ref$maxHeight = _ref.maxHeight,
|
|
20
20
|
maxHeight = _ref$maxHeight === void 0 ? 'min(50vh, 640px)' : _ref$maxHeight,
|
|
21
21
|
open = _ref.open,
|
|
22
|
+
_ref$placement = _ref.placement,
|
|
23
|
+
placement = _ref$placement === void 0 ? 'top' : _ref$placement,
|
|
22
24
|
customStyles = _ref.styles,
|
|
23
25
|
classNames = _ref.classNames;
|
|
24
26
|
var parent = getPopupContainer();
|
|
25
27
|
if (!parent) return;
|
|
26
28
|
if (!open) return;
|
|
29
|
+
var rootClassName = placement === 'bottom' ? styles.rootBottom : styles.rootTop;
|
|
27
30
|
var node = /*#__PURE__*/_jsx(Flexbox, {
|
|
28
|
-
className: cx(
|
|
31
|
+
className: cx(rootClassName, classNames === null || classNames === void 0 ? void 0 : classNames.root),
|
|
29
32
|
paddingInline: 8,
|
|
30
33
|
style: customStyles === null || customStyles === void 0 ? void 0 : customStyles.root,
|
|
31
34
|
width: '100%',
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
var _templateObject, _templateObject2, _templateObject3;
|
|
1
|
+
var _templateObject, _templateObject2, _templateObject3, _templateObject4;
|
|
2
2
|
function _taggedTemplateLiteral(strings, raw) { if (!raw) { raw = strings.slice(0); } return Object.freeze(Object.defineProperties(strings, { raw: { value: Object.freeze(raw) } })); }
|
|
3
3
|
import { createStaticStyles } from 'antd-style';
|
|
4
4
|
export var styles = createStaticStyles(function (_ref) {
|
|
@@ -7,6 +7,7 @@ export var styles = createStaticStyles(function (_ref) {
|
|
|
7
7
|
return {
|
|
8
8
|
container: css(_templateObject || (_templateObject = _taggedTemplateLiteral(["\n position: relative;\n overflow: hidden auto;\n background: ", ";\n "])), cssVar.colorBgElevated),
|
|
9
9
|
containerWithMaxHeight: css(_templateObject2 || (_templateObject2 = _taggedTemplateLiteral(["\n /* maxHeight is set via inline style as it's dynamic */\n "]))),
|
|
10
|
-
|
|
10
|
+
rootBottom: css(_templateObject3 || (_templateObject3 = _taggedTemplateLiteral(["\n position: absolute;\n z-index: 9999;\n inset-block-start: 100%;\n inset-inline-start: 0;\n\n padding-block-start: 8px;\n "]))),
|
|
11
|
+
rootTop: css(_templateObject4 || (_templateObject4 = _taggedTemplateLiteral(["\n position: absolute;\n inset-block-start: -8px;\n inset-inline-start: 0;\n transform: translateY(-100%);\n "])))
|
|
11
12
|
};
|
|
12
13
|
});
|
|
@@ -11,6 +11,8 @@ export interface FloatMenuProps {
|
|
|
11
11
|
getPopupContainer: () => HTMLDivElement | null;
|
|
12
12
|
maxHeight?: string | number;
|
|
13
13
|
open?: boolean;
|
|
14
|
+
/** Menu placement direction: 'top' (default) or 'bottom' */
|
|
15
|
+
placement?: 'bottom' | 'top';
|
|
14
16
|
style?: CSSProperties;
|
|
15
17
|
styles?: {
|
|
16
18
|
container?: CSSProperties;
|
package/package.json
CHANGED