@lobehub/editor 1.7.1 → 1.8.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.
@@ -15,7 +15,7 @@ function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.g
15
15
  function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
16
16
  function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : String(i); }
17
17
  function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
18
- import { $getSelection, $isRangeSelection } from 'lexical';
18
+ import { $getSelection, $isRangeSelection, COMMAND_PRIORITY_CRITICAL, KEY_DOWN_COMMAND } from 'lexical';
19
19
  import { KernelPlugin } from "../../../editor-kernel/plugin";
20
20
  import { ISlashService, SlashService } from "../service/i-slash-service";
21
21
  import { getQueryTextForSearch, tryToPositionRange } from "../utils/utils";
@@ -29,6 +29,7 @@ export var SlashPlugin = (_class = /*#__PURE__*/function (_KernelPlugin) {
29
29
  _defineProperty(_assertThisInitialized(_this), "service", null);
30
30
  _defineProperty(_assertThisInitialized(_this), "currentSlashTrigger", null);
31
31
  _defineProperty(_assertThisInitialized(_this), "currentSlashTriggerIndex", -1);
32
+ _defineProperty(_assertThisInitialized(_this), "suppressOpen", false);
32
33
  _this.config = config;
33
34
  _this.service = new SlashService(kernel);
34
35
  kernel.registerService(ISlashService, _this.service);
@@ -47,11 +48,23 @@ export var SlashPlugin = (_class = /*#__PURE__*/function (_KernelPlugin) {
47
48
  (_this$config = this.config) === null || _this$config === void 0 || _this$config.triggerClose();
48
49
  this.currentSlashTrigger = null;
49
50
  this.currentSlashTriggerIndex = -1;
51
+ // After an explicit close, suppress reopening until next typing input
52
+ this.suppressOpen = true;
50
53
  }
51
54
  }, {
52
55
  key: "onInit",
53
56
  value: function onInit(editor) {
54
57
  var _this2 = this;
58
+ // Reset suppression on typing-related key presses
59
+ this.register(editor.registerCommand(KEY_DOWN_COMMAND, function (event) {
60
+ if (event.isComposing) return false;
61
+ var key = event.key;
62
+ // Any character input or deletion should re-enable opening
63
+ if (key.length === 1 || key === 'Backspace' || key === 'Delete') {
64
+ _this2.suppressOpen = false;
65
+ }
66
+ return false;
67
+ }, COMMAND_PRIORITY_CRITICAL));
55
68
  this.register(editor.registerUpdateListener(function () {
56
69
  editor.getEditorState().read(function () {
57
70
  var _this2$service, _this2$service2, _this2$service3;
@@ -79,6 +92,12 @@ export var SlashPlugin = (_class = /*#__PURE__*/function (_KernelPlugin) {
79
92
  _this2.triggerClose();
80
93
  return;
81
94
  }
95
+
96
+ // If we previously suppressed opening, do not reopen until user types
97
+ if (_this2.currentSlashTrigger === null && _this2.suppressOpen) {
98
+ _this2.triggerClose();
99
+ return;
100
+ }
82
101
  var triggerText = _this2.currentSlashTrigger;
83
102
  if (triggerText === null) {
84
103
  triggerText = text.slice(-1);
@@ -101,6 +120,14 @@ export var SlashPlugin = (_class = /*#__PURE__*/function (_KernelPlugin) {
101
120
  var fuse = (_this2$service3 = _this2.service) === null || _this2$service3 === void 0 ? void 0 : _this2$service3.getSlashFuse(triggerText);
102
121
  var isRangePositioned = tryToPositionRange(_this2.currentSlashTriggerIndex, range, editorWindow);
103
122
  var match = triggerFn === null || triggerFn === void 0 ? void 0 : triggerFn(text.slice(_this2.currentSlashTriggerIndex));
123
+
124
+ // Check if there's a space in the current search text that should close the menu
125
+ var searchText = text.slice(_this2.currentSlashTriggerIndex);
126
+ var hasSpaceAfterTrigger = searchText.includes(' ') && !(slashOptions !== null && slashOptions !== void 0 && slashOptions.allowWhitespace);
127
+ if (hasSpaceAfterTrigger) {
128
+ _this2.triggerClose();
129
+ return;
130
+ }
104
131
  var finalItems = fuse && match && match.matchingString.length > 0 ? fuse.search(match.matchingString).map(function (result) {
105
132
  return result.item;
106
133
  }) : slashOptions.items;
@@ -1,8 +1,20 @@
1
+ import type { IFuseOptions } from 'fuse.js';
1
2
  import type { FC, ReactElement } from 'react';
2
3
  import type { SlashOptions } from "..";
3
4
  import type { ISlashMenuOption, ISlashOption } from "../service/i-slash-service";
4
5
  import type { IEditor } from "../../../types";
5
6
  export interface ReactSlashOptionProps {
7
+ /**
8
+ * Complete Fuse.js configuration options
9
+ * @example
10
+ * {
11
+ * keys: ['key', 'label', 'description'],
12
+ * threshold: 0.3,
13
+ * includeScore: true,
14
+ * includeMatches: true
15
+ * }
16
+ */
17
+ fuseOptions?: IFuseOptions<ISlashMenuOption>;
6
18
  /**
7
19
  * Searchable options
8
20
  */
@@ -17,6 +29,13 @@ export interface ReactSlashOptionProps {
17
29
  * Custom render component
18
30
  */
19
31
  renderComp?: FC<MenuRenderProps>;
32
+ /**
33
+ * Fuse.js search keys for fuzzy matching
34
+ * Default is ['key']
35
+ * @example ['key', 'label', 'description']
36
+ * @deprecated Use fuseOptions instead
37
+ */
38
+ searchKeys?: string[];
20
39
  /**
21
40
  * Trigger character
22
41
  */
@@ -1,17 +1,28 @@
1
1
  import { DropdownMenuItemType } from '@lobehub/ui';
2
- import Fuse from 'fuse.js';
2
+ import Fuse, { type IFuseOptions } from 'fuse.js';
3
3
  import type { IEditor, IEditorKernel, IServiceID } from "../../../types";
4
4
  import { getBasicTypeaheadTriggerMatch } from '../utils/utils';
5
5
  export type ISlashDividerOption = {
6
6
  type: 'divider';
7
7
  };
8
- export interface ISlashMenuOption extends Omit<DropdownMenuItemType, 'extra'> {
8
+ export interface ISlashMenuOption extends DropdownMenuItemType {
9
9
  metadata?: Record<string, any>;
10
10
  onSelect?: (editor: IEditor, matchingString: string) => void;
11
11
  }
12
12
  export type ISlashOption = ISlashMenuOption | ISlashDividerOption;
13
13
  export interface SlashOptions {
14
14
  allowWhitespace?: boolean;
15
+ /**
16
+ * Complete Fuse.js configuration options
17
+ * @example
18
+ * {
19
+ * keys: ['key', 'label', 'description'],
20
+ * threshold: 0.3,
21
+ * includeScore: true,
22
+ * includeMatches: true
23
+ * }
24
+ */
25
+ fuseOptions?: IFuseOptions<ISlashMenuOption>;
15
26
  items: Array<ISlashOption> | ((search: {
16
27
  leadOffset: number;
17
28
  matchingString: string;
@@ -20,6 +31,13 @@ export interface SlashOptions {
20
31
  maxLength?: number;
21
32
  minLength?: number;
22
33
  punctuation?: string;
34
+ /**
35
+ * Fuse.js search keys for fuzzy matching
36
+ * Default is ['key']
37
+ * @example ['key', 'label', 'description']
38
+ * @deprecated Use fuseOptions instead
39
+ */
40
+ searchKeys?: string[];
23
41
  trigger: string;
24
42
  }
25
43
  export interface ISlashService {
@@ -46,9 +46,12 @@ export var SlashService = /*#__PURE__*/function () {
46
46
  var searchableItems = options.items.filter(function (item) {
47
47
  return !('type' in item) || item.type !== 'divider';
48
48
  });
49
- this.triggerFuseMap.set(options.trigger, new Fuse(searchableItems, {
50
- keys: ['label', 'value']
51
- }));
49
+
50
+ // Use fuseOptions if provided, otherwise fallback to searchKeys or default
51
+ var fuseConfig = options.fuseOptions || {
52
+ keys: options.searchKeys || ['key']
53
+ };
54
+ this.triggerFuseMap.set(options.trigger, new Fuse(searchableItems, fuseConfig));
52
55
  }
53
56
  }
54
57
  }, {
@@ -120,14 +120,20 @@ export function getBasicTypeaheadTriggerMatch(trigger, _ref) {
120
120
  _ref$allowWhitespace = _ref.allowWhitespace,
121
121
  allowWhitespace = _ref$allowWhitespace === void 0 ? false : _ref$allowWhitespace;
122
122
  return function (text) {
123
+ // When whitespace is not allowed, we need to ensure the regex stops at whitespace
123
124
  var validCharsSuffix = allowWhitespace ? '' : '\\s';
124
125
  var validChars = '[^' + trigger + punctuation + validCharsSuffix + ']';
125
- var TypeaheadTriggerRegex = new RegExp('(^|\\s|\\()(' + '[' + trigger + ']' + '((?:' + validChars + '){0,' + maxLength + '})' + ')$');
126
+
127
+ // Create regex that matches from trigger to either end of string OR whitespace (when not allowed)
128
+ var regexPattern = allowWhitespace ? '(^|\\s|\\()(' + '[' + trigger + ']' + '((?:' + validChars + '){0,' + maxLength + '})' + ')$' : '(^|\\s|\\()(' + '[' + trigger + ']' + '((?:' + validChars + ')*?)' + ')(?=\\s|$)';
129
+ var TypeaheadTriggerRegex = new RegExp(regexPattern);
126
130
  var match = TypeaheadTriggerRegex.exec(text);
127
131
  if (match !== null) {
128
132
  var maybeLeadingWhitespace = match[1];
129
133
  var matchingString = match[3];
130
- if (matchingString.length >= minLength) {
134
+
135
+ // Check length constraints
136
+ if (matchingString.length >= minLength && matchingString.length <= maxLength) {
131
137
  return {
132
138
  leadOffset: match.index + maybeLeadingWhitespace.length,
133
139
  matchingString: matchingString,
@@ -472,7 +472,9 @@ export default /*#__PURE__*/memo(function (_ref3) {
472
472
  if (typeof document === 'undefined') {
473
473
  return null;
474
474
  }
475
- var targetElement = anchorElem || document.body;
475
+ var root = editor.getRootElement();
476
+ var anchor = root ? root.parentElement : null;
477
+ var targetElement = anchorElem || anchor || document.body;
476
478
  return /*#__PURE__*/createPortal( /*#__PURE__*/_jsx(TableCellActionMenuContainer, {
477
479
  anchorElem: targetElement,
478
480
  cellMerge: cellMerge,
@@ -3,5 +3,5 @@ function _taggedTemplateLiteral(strings, raw) { if (!raw) { raw = strings.slice(
3
3
  import { createStyles } from 'antd-style';
4
4
  export var useStyles = createStyles(function (_ref) {
5
5
  var css = _ref.css;
6
- return css(_templateObject || (_templateObject = _taggedTemplateLiteral(["\n will-change: transform;\n\n position: absolute;\n z-index: 3;\n inset-block-start: 0;\n inset-inline-start: 0;\n "])));
6
+ return css(_templateObject || (_templateObject = _taggedTemplateLiteral(["\n will-change: transform;\n\n position: absolute;\n z-index: 3;\n inset-block-start: 0;\n inset-inline-start: 0;\n\n background: red;\n "])));
7
7
  });
@@ -19,7 +19,7 @@ import { PlusIcon } from 'lucide-react';
19
19
  import { memo, useEffect, useMemo, useRef, useState } from 'react';
20
20
  import { createPortal } from 'react-dom';
21
21
  import { useLexicalComposerContext } from "../../../../editor-kernel/react";
22
- import { BUTTON_WIDTH_PX, useStyles } from "./style";
22
+ import { useStyles } from "./style";
23
23
  import { getMouseInfo, useDebounce } from "./utils";
24
24
  import { jsx as _jsx } from "react/jsx-runtime";
25
25
  import { jsxs as _jsxs } from "react/jsx-runtime";
@@ -94,12 +94,10 @@ var TableHoverActionsContainer = /*#__PURE__*/memo(function (_ref) {
94
94
  });
95
95
  if (tableDOMElement) {
96
96
  var _getBoundingClientRec = tableDOMElement.getBoundingClientRect(),
97
- tableElemWidth = _getBoundingClientRec.width,
98
97
  tableElemY = _getBoundingClientRec.y,
99
98
  tableElemRight = _getBoundingClientRec.right,
100
99
  tableElemLeft = _getBoundingClientRec.left,
101
- tableElemBottom = _getBoundingClientRec.bottom,
102
- tableElemHeight = _getBoundingClientRec.height;
100
+ tableElemBottom = _getBoundingClientRec.bottom;
103
101
 
104
102
  // Adjust for using the scrollable table container
105
103
  var parentElement = tableDOMElement.parentElement;
@@ -114,19 +112,15 @@ var TableHoverActionsContainer = /*#__PURE__*/memo(function (_ref) {
114
112
  setShownColumn(false);
115
113
  setShownRow(true);
116
114
  setPosition({
117
- height: BUTTON_WIDTH_PX,
118
115
  left: tableHasScroll && parentElement ? parentElement.offsetLeft : tableElemLeft - editorElemLeft,
119
- top: tableElemBottom - editorElemY + 5,
120
- width: tableHasScroll && parentElement ? parentElement.offsetWidth : tableElemWidth
116
+ top: tableElemBottom - editorElemY + 5
121
117
  });
122
118
  } else if (hoveredColumnNode) {
123
119
  setShownColumn(true);
124
120
  setShownRow(false);
125
121
  setPosition({
126
- height: tableElemHeight,
127
122
  left: tableElemRight - editorElemLeft + 5,
128
- top: tableElemY - editorElemY,
129
- width: BUTTON_WIDTH_PX
123
+ top: tableElemY - editorElemY
130
124
  });
131
125
  }
132
126
  }
@@ -260,7 +254,9 @@ export default /*#__PURE__*/memo(function (_ref2) {
260
254
  if (typeof document === 'undefined') {
261
255
  return null;
262
256
  }
263
- var targetElement = anchorElem || document.body;
257
+ var root = editor.getRootElement();
258
+ var anchor = root ? root.parentElement : null;
259
+ var targetElement = anchorElem || anchor || document.body;
264
260
  return /*#__PURE__*/createPortal( /*#__PURE__*/_jsx(TableHoverActionsContainer, {
265
261
  anchorElem: targetElement,
266
262
  editor: editor
@@ -5,7 +5,7 @@ export var BUTTON_WIDTH_PX = 20;
5
5
  export var useStyles = createStyles(function (_ref) {
6
6
  var css = _ref.css;
7
7
  return {
8
- tableAddColumns: css(_templateObject || (_templateObject = _taggedTemplateLiteral(["\n position: absolute;\n height: 100%;\n "]))),
9
- tableAddRows: css(_templateObject2 || (_templateObject2 = _taggedTemplateLiteral(["\n position: absolute;\n width: calc(100% - 25px);\n "])))
8
+ tableAddColumns: css(_templateObject || (_templateObject = _taggedTemplateLiteral(["\n position: absolute;\n "]))),
9
+ tableAddRows: css(_templateObject2 || (_templateObject2 = _taggedTemplateLiteral(["\n position: absolute;\n "])))
10
10
  };
11
11
  });
@@ -1,50 +1,152 @@
1
1
  'use client';
2
2
 
3
3
  function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
4
- var _excluded = ["maxHeight", "className", "children", "footer", "header", "style", "slashMenuRef", "classNames", "fullscreen", "styles"];
4
+ var _excluded = ["defaultHeight", "height", "maxHeight", "minHeight", "resizeMaxHeightOffset", "resize", "onSizeChange", "onSizeDragging", "className", "children", "footer", "header", "style", "slashMenuRef", "classNames", "fullscreen", "showResizeHandle", "styles"];
5
5
  function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
6
6
  function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
7
7
  function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
8
8
  function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : String(i); }
9
9
  function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
10
+ function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }
11
+ function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
12
+ 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); }
13
+ 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; }
14
+ function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }
15
+ function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
10
16
  function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }
11
17
  function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }
12
- import { memo } from 'react';
18
+ import { Resizable } from 're-resizable';
19
+ import { memo, useCallback } from 'react';
13
20
  import { Flexbox } from 'react-layout-kit';
21
+ import useMergeState from 'use-merge-value';
22
+ import { useHeight } from "../hooks/useSize";
14
23
  import { useStyles } from "./style";
15
24
  import { jsx as _jsx } from "react/jsx-runtime";
16
25
  import { jsxs as _jsxs } from "react/jsx-runtime";
17
- var ChatInput = /*#__PURE__*/memo(function (_ref) {
18
- var _ref$maxHeight = _ref.maxHeight,
19
- maxHeight = _ref$maxHeight === void 0 ? 'min(50vh, 640px)' : _ref$maxHeight,
20
- className = _ref.className,
21
- children = _ref.children,
22
- footer = _ref.footer,
23
- header = _ref.header,
24
- style = _ref.style,
25
- slashMenuRef = _ref.slashMenuRef,
26
- classNames = _ref.classNames,
27
- fullscreen = _ref.fullscreen,
28
- customStyles = _ref.styles,
29
- rest = _objectWithoutProperties(_ref, _excluded);
26
+ var ChatInput = /*#__PURE__*/memo(function (props) {
27
+ var _props$defaultHeight = props.defaultHeight,
28
+ defaultHeight = _props$defaultHeight === void 0 ? props.defaultHeight || props.minHeight || 64 : _props$defaultHeight,
29
+ height = props.height,
30
+ _props$maxHeight = props.maxHeight,
31
+ maxHeight = _props$maxHeight === void 0 ? 320 : _props$maxHeight,
32
+ _props$minHeight = props.minHeight,
33
+ minHeight = _props$minHeight === void 0 ? 64 : _props$minHeight,
34
+ _props$resizeMaxHeigh = props.resizeMaxHeightOffset,
35
+ resizeMaxHeightOffset = _props$resizeMaxHeigh === void 0 ? 120 : _props$resizeMaxHeigh,
36
+ _props$resize = props.resize,
37
+ resize = _props$resize === void 0 ? true : _props$resize,
38
+ onSizeChange = props.onSizeChange,
39
+ onSizeDragging = props.onSizeDragging,
40
+ className = props.className,
41
+ children = props.children,
42
+ footer = props.footer,
43
+ header = props.header,
44
+ style = props.style,
45
+ slashMenuRef = props.slashMenuRef,
46
+ classNames = props.classNames,
47
+ fullscreen = props.fullscreen,
48
+ showResizeHandle = props.showResizeHandle,
49
+ customStyles = props.styles,
50
+ rest = _objectWithoutProperties(props, _excluded);
30
51
  var _useStyles = useStyles(),
31
52
  cx = _useStyles.cx,
32
53
  styles = _useStyles.styles;
54
+ var _useHeight = useHeight(),
55
+ headerRef = _useHeight.ref,
56
+ _useHeight$height = _useHeight.height,
57
+ headerHeight = _useHeight$height === void 0 ? 0 : _useHeight$height;
58
+
59
+ // 使用 useMergeState 管理高度状态
60
+ var _useMergeState = useMergeState(defaultHeight, {
61
+ defaultValue: defaultHeight,
62
+ onChange: onSizeChange,
63
+ value: height
64
+ }),
65
+ _useMergeState2 = _slicedToArray(_useMergeState, 2),
66
+ currentHeight = _useMergeState2[0],
67
+ setCurrentHeight = _useMergeState2[1];
68
+
69
+ // 处理尺寸变化
70
+ var handleResizeStop = useCallback(function (e, direction, ref) {
71
+ var newHeight = ref.style.height ? parseInt(ref.style.height) : defaultHeight;
72
+ setCurrentHeight(newHeight);
73
+ }, [setCurrentHeight, defaultHeight]);
74
+
75
+ // 处理拖拽过程中的尺寸变化
76
+ var handleResize = useCallback(function (e, direction, ref) {
77
+ var newHeight = ref.style.height ? parseInt(ref.style.height) : defaultHeight;
78
+ onSizeDragging === null || onSizeDragging === void 0 || onSizeDragging(newHeight);
79
+ }, [onSizeDragging, defaultHeight]);
80
+ var bodyNode = /*#__PURE__*/_jsx("div", {
81
+ className: cx(styles.editor, classNames === null || classNames === void 0 ? void 0 : classNames.body),
82
+ style: _objectSpread(_objectSpread({}, customStyles === null || customStyles === void 0 ? void 0 : customStyles.body), {}, {
83
+ flex: 1,
84
+ maxHeight: fullscreen ? '100%' : maxHeight,
85
+ minHeight: resize ? currentHeight : minHeight,
86
+ zIndex: 0
87
+ }),
88
+ children: children
89
+ });
33
90
  return /*#__PURE__*/_jsxs(Flexbox, _objectSpread(_objectSpread({
34
91
  className: cx(styles.container, className),
35
92
  height: fullscreen ? '100%' : undefined,
36
93
  style: _objectSpread({
37
- maxHeight: fullscreen ? undefined : maxHeight
94
+ position: 'relative'
38
95
  }, style),
39
- width: '100%'
96
+ width: "100%"
40
97
  }, rest), {}, {
41
98
  children: [slashMenuRef && /*#__PURE__*/_jsx("div", {
42
99
  ref: slashMenuRef
43
- }), header, /*#__PURE__*/_jsx("div", {
44
- className: cx(styles.editor, classNames === null || classNames === void 0 ? void 0 : classNames.body),
45
- style: customStyles === null || customStyles === void 0 ? void 0 : customStyles.body,
46
- children: children
47
- }), footer]
100
+ }), /*#__PURE__*/_jsx("div", {
101
+ className: classNames === null || classNames === void 0 ? void 0 : classNames.header,
102
+ ref: headerRef,
103
+ style: _objectSpread({
104
+ width: '100%',
105
+ zIndex: 1
106
+ }, customStyles === null || customStyles === void 0 ? void 0 : customStyles.header),
107
+ children: header
108
+ }), resize ? /*#__PURE__*/_jsx(Resizable, {
109
+ className: styles.resizableContainer,
110
+ enable: fullscreen ? false : {
111
+ top: true
112
+ },
113
+ handleClasses: {
114
+ top: showResizeHandle ? styles.resizeHandle : undefined
115
+ },
116
+ handleStyles: {
117
+ top: {
118
+ backgroundColor: 'transparent',
119
+ borderRadius: '4px',
120
+ cursor: 'ns-resize',
121
+ height: '8px',
122
+ left: '50%',
123
+ top: !!header ? -3 - headerHeight : -3,
124
+ transform: 'translateX(-50%)',
125
+ width: '100%'
126
+ }
127
+ },
128
+ maxHeight: fullscreen ? undefined : maxHeight + resizeMaxHeightOffset,
129
+ minHeight: fullscreen ? undefined : minHeight,
130
+ onResize: handleResize,
131
+ onResizeStop: handleResizeStop,
132
+ size: {
133
+ height: fullscreen ? undefined : 'auto',
134
+ width: '100%'
135
+ },
136
+ style: fullscreen ? {
137
+ flex: 1,
138
+ overflow: 'hidden',
139
+ position: 'relative'
140
+ } : {},
141
+ children: bodyNode
142
+ }) : bodyNode, /*#__PURE__*/_jsx("div", {
143
+ className: classNames === null || classNames === void 0 ? void 0 : classNames.footer,
144
+ style: _objectSpread({
145
+ width: '100%',
146
+ zIndex: 1
147
+ }, customStyles === null || customStyles === void 0 ? void 0 : customStyles.footer),
148
+ children: footer
149
+ })]
48
150
  }));
49
151
  });
50
152
  ChatInput.displayName = 'ChatInput';
@@ -1,4 +1,6 @@
1
1
  export declare const useStyles: (props?: unknown) => import("antd-style").ReturnStyles<{
2
2
  container: import("antd-style").SerializedStyles;
3
3
  editor: import("antd-style").SerializedStyles;
4
+ resizableContainer: import("antd-style").SerializedStyles;
5
+ resizeHandle: import("antd-style").SerializedStyles;
4
6
  }>;
@@ -1,11 +1,13 @@
1
- var _templateObject, _templateObject2;
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 { createStyles } from 'antd-style';
4
4
  export var useStyles = createStyles(function (_ref) {
5
5
  var css = _ref.css,
6
6
  token = _ref.token;
7
7
  return {
8
- container: css(_templateObject || (_templateObject = _taggedTemplateLiteral(["\n position: relative;\n\n border: 1px solid ", ";\n border-radius: ", "px;\n\n background-color: ", ";\n box-shadow:\n ", ",\n 0 32px 0 ", ";\n "])), token.colorBorderSecondary, token.borderRadiusLG, token.colorBgContainer, token.boxShadowTertiary, token.colorBgContainerSecondary),
9
- editor: css(_templateObject2 || (_templateObject2 = _taggedTemplateLiteral(["\n overflow: hidden auto;\n flex: 1;\n\n width: 100%;\n padding-block: 8px 0;\n padding-inline: 12px;\n "])))
8
+ container: css(_templateObject || (_templateObject = _taggedTemplateLiteral(["\n position: relative;\n\n display: flex;\n flex-direction: column;\n\n height: 100%;\n border: 1px solid ", ";\n border-radius: ", "px;\n\n background-color: ", ";\n box-shadow:\n ", ",\n 0 32px 0 ", ";\n "])), token.colorBorderSecondary, token.borderRadiusLG, token.colorBgContainer, token.boxShadowTertiary, token.colorBgContainerSecondary),
9
+ editor: css(_templateObject2 || (_templateObject2 = _taggedTemplateLiteral(["\n overflow: hidden auto;\n flex: 1;\n\n width: 100%;\n padding-block: 8px 0;\n padding-inline: 12px;\n "]))),
10
+ resizableContainer: css(_templateObject3 || (_templateObject3 = _taggedTemplateLiteral(["\n position: relative;\n\n display: flex;\n flex-direction: column;\n align-self: flex-end;\n\n width: 100%;\n\n &:hover .resize-handle {\n opacity: 1;\n }\n "]))),
11
+ resizeHandle: css(_templateObject4 || (_templateObject4 = _taggedTemplateLiteral(["\n position: absolute;\n inset-block-start: -4px;\n inset-inline-start: 50%;\n transform: translateX(-50%);\n\n width: 100%;\n height: 8px;\n\n opacity: 0;\n\n transition: opacity 0.2s ease-in-out;\n\n &::before {\n content: '';\n\n position: absolute;\n inset-block-start: 0;\n inset-inline-start: 50%;\n transform: translateX(-50%);\n\n width: 32px;\n height: 4px;\n border-radius: 4px;\n\n background-color: ", ";\n box-shadow: 0 1px 2px ", "20;\n }\n\n &:hover {\n opacity: 1 !important;\n\n &::before {\n background-color: ", ";\n box-shadow: 0 2px 4px ", "40;\n }\n }\n\n &:active {\n &::before {\n background-color: ", ";\n }\n }\n "])), token.colorPrimary, token.colorTextSecondary, token.colorPrimaryHover, token.colorTextSecondary, token.colorPrimaryActive)
10
12
  };
11
13
  });
@@ -1,15 +1,27 @@
1
1
  import type { CSSProperties, ReactNode, Ref } from 'react';
2
2
  import { FlexboxProps } from 'react-layout-kit';
3
- export interface ChatInputProps extends FlexboxProps {
3
+ export interface ChatInputProps extends Omit<FlexboxProps, 'height'> {
4
4
  classNames?: {
5
5
  body?: string;
6
+ footer?: string;
7
+ header?: string;
6
8
  };
9
+ defaultHeight?: number;
7
10
  footer?: ReactNode;
8
11
  fullscreen?: boolean;
9
12
  header?: ReactNode;
10
- maxHeight?: string | number;
13
+ height?: number;
14
+ maxHeight?: number;
15
+ minHeight?: number;
16
+ onSizeChange?: (height: number) => void;
17
+ onSizeDragging?: (height: number) => void;
18
+ resize?: boolean;
19
+ resizeMaxHeightOffset?: number;
20
+ showResizeHandle?: boolean;
11
21
  slashMenuRef?: Ref<HTMLDivElement>;
12
22
  styles?: {
13
23
  body?: CSSProperties;
24
+ footer?: CSSProperties;
25
+ header?: CSSProperties;
14
26
  };
15
27
  }
@@ -8,6 +8,6 @@ interface UseDisplayActionCountOptions {
8
8
  export declare const useDisplayActionCount: ({ items, collapseOffset, autoCollapse, }?: UseDisplayActionCountOptions) => {
9
9
  collapsed: boolean;
10
10
  maxCount: number;
11
- ref: import("react").RefObject<HTMLElement | null>;
11
+ ref: import("react").RefObject<HTMLDivElement | null>;
12
12
  };
13
13
  export {};
@@ -5,7 +5,7 @@ function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len
5
5
  function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }
6
6
  function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
7
7
  import { useEffect, useMemo, useState } from 'react';
8
- import { useContainerSize } from "./useContainerSize";
8
+ import { useWidth } from "../../hooks/useSize";
9
9
  export var useDisplayActionCount = function useDisplayActionCount() {
10
10
  var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
11
11
  _ref$items = _ref.items,
@@ -13,9 +13,9 @@ export var useDisplayActionCount = function useDisplayActionCount() {
13
13
  _ref$collapseOffset = _ref.collapseOffset,
14
14
  collapseOffset = _ref$collapseOffset === void 0 ? 0 : _ref$collapseOffset,
15
15
  autoCollapse = _ref.autoCollapse;
16
- var _useContainerSize = useContainerSize(),
17
- ref = _useContainerSize.ref,
18
- size = _useContainerSize.size;
16
+ var _useWidth = useWidth(),
17
+ ref = _useWidth.ref,
18
+ width = _useWidth.width;
19
19
  var _useState = useState(false),
20
20
  _useState2 = _slicedToArray(_useState, 2),
21
21
  collapsed = _useState2[0],
@@ -48,14 +48,14 @@ export var useDisplayActionCount = function useDisplayActionCount() {
48
48
  setMaxCount(rawMaxCount);
49
49
  return;
50
50
  }
51
- if (!size) return;
51
+ if (!width) return;
52
52
  var atLeastCount = 1 + alwaysDisplayCount;
53
- var calcMaxCount = Math.floor((size - collapseOffset) / 38);
53
+ var calcMaxCount = Math.floor((width - collapseOffset) / 38);
54
54
  if (calcMaxCount < atLeastCount) calcMaxCount = atLeastCount;
55
55
  setCollapsed(calcMaxCount < rawMaxCount);
56
56
  if (calcMaxCount >= rawMaxCount) return;
57
57
  setMaxCount(calcMaxCount);
58
- }, [autoCollapse, size, rawMaxCount, collapseOffset, alwaysDisplayCount]);
58
+ }, [autoCollapse, width, rawMaxCount, collapseOffset, alwaysDisplayCount]);
59
59
  return useMemo(function () {
60
60
  return {
61
61
  collapsed: collapsed,
@@ -71,10 +71,10 @@ var Editor = /*#__PURE__*/memo(function (_ref) {
71
71
  if (!enableSlash && !enableMention) return null;
72
72
  return /*#__PURE__*/_jsxs(ReactSlashPlugin, {
73
73
  children: [enableSlash ? /*#__PURE__*/_jsx(ReactSlashOption, _objectSpread({
74
- maxLength: 1,
74
+ maxLength: 8,
75
75
  trigger: "/"
76
76
  }, slashOption)) : undefined, enableMention ? /*#__PURE__*/_jsx(ReactSlashOption, _objectSpread({
77
- maxLength: 6,
77
+ maxLength: 8,
78
78
  trigger: "@"
79
79
  }, restMentionOption)) : undefined]
80
80
  });
@@ -0,0 +1,13 @@
1
+ /// <reference types="react" />
2
+ interface UseContainerSizeOptions {
3
+ debounceMs?: number;
4
+ }
5
+ export declare const useWidth: (options?: UseContainerSizeOptions) => {
6
+ ref: import("react").RefObject<HTMLDivElement | null>;
7
+ width: number | undefined;
8
+ };
9
+ export declare const useHeight: (options?: UseContainerSizeOptions) => {
10
+ height: number | undefined;
11
+ ref: import("react").RefObject<HTMLDivElement | null>;
12
+ };
13
+ export {};
@@ -7,7 +7,7 @@ function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
7
7
  // hooks/useContainerSize.ts
8
8
  import { debounce } from 'lodash-es';
9
9
  import { useCallback, useEffect, useRef, useState } from 'react';
10
- export var useContainerSize = function useContainerSize() {
10
+ export var useWidth = function useWidth() {
11
11
  var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
12
12
  var _options$debounceMs = options.debounceMs,
13
13
  debounceMs = _options$debounceMs === void 0 ? 100 : _options$debounceMs;
@@ -32,12 +32,46 @@ export var useContainerSize = function useContainerSize() {
32
32
  if (resizeObserverRef.current) {
33
33
  var _updateSize$cancel;
34
34
  resizeObserverRef.current.disconnect();
35
- (_updateSize$cancel = updateSize.cancel) === null || _updateSize$cancel === void 0 || _updateSize$cancel.call(updateSize); // 清理防抖
35
+ (_updateSize$cancel = updateSize.cancel) === null || _updateSize$cancel === void 0 || _updateSize$cancel.call(updateSize);
36
36
  }
37
37
  };
38
38
  }, [updateSize]);
39
39
  return {
40
40
  ref: ref,
41
- size: size
41
+ width: size
42
+ };
43
+ };
44
+ export var useHeight = function useHeight() {
45
+ var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
46
+ var _options$debounceMs2 = options.debounceMs,
47
+ debounceMs = _options$debounceMs2 === void 0 ? 100 : _options$debounceMs2;
48
+ var ref = useRef(null);
49
+ var _useState3 = useState(),
50
+ _useState4 = _slicedToArray(_useState3, 2),
51
+ size = _useState4[0],
52
+ setSize = _useState4[1];
53
+ var resizeObserverRef = useRef(null);
54
+ var updateSize = useCallback(debounce(function (entries) {
55
+ if (entries[0]) {
56
+ var height = entries[0].contentRect.height;
57
+ setSize(Math.floor(height));
58
+ }
59
+ }, debounceMs), [debounceMs]);
60
+ useEffect(function () {
61
+ var element = ref.current;
62
+ if (!element) return;
63
+ resizeObserverRef.current = new ResizeObserver(updateSize);
64
+ resizeObserverRef.current.observe(element);
65
+ return function () {
66
+ if (resizeObserverRef.current) {
67
+ var _updateSize$cancel2;
68
+ resizeObserverRef.current.disconnect();
69
+ (_updateSize$cancel2 = updateSize.cancel) === null || _updateSize$cancel2 === void 0 || _updateSize$cancel2.call(updateSize);
70
+ }
71
+ };
72
+ }, [updateSize]);
73
+ return {
74
+ height: size,
75
+ ref: ref
42
76
  };
43
77
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lobehub/editor",
3
- "version": "1.7.1",
3
+ "version": "1.8.1",
4
4
  "description": "A powerful and extensible rich text editor built on Meta's Lexical framework, providing a modern editing experience with React integration.",
5
5
  "keywords": [
6
6
  "lobehub",
@@ -52,6 +52,7 @@
52
52
  "lodash-es": "^4.17.21",
53
53
  "lucide-react": "^0.536.0",
54
54
  "polished": "^4.3.1",
55
+ "re-resizable": "^6.11.2",
55
56
  "react-error-boundary": "^6.0.0",
56
57
  "react-layout-kit": "^2.0.0",
57
58
  "react-merge-refs": "^3.0.2",
@@ -1,9 +0,0 @@
1
- /// <reference types="react" />
2
- interface UseContainerSizeOptions {
3
- debounceMs?: number;
4
- }
5
- export declare const useContainerSize: (options?: UseContainerSizeOptions) => {
6
- ref: import("react").RefObject<HTMLElement | null>;
7
- size: number | undefined;
8
- };
9
- export {};