@ctzhian/tiptap 1.12.21 → 1.12.23

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.
@@ -1,4 +1,16 @@
1
- import { updatePosition } from "../../util";
1
+ 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); }
2
+ 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; }
3
+ 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; }
4
+ 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; }
5
+ function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : String(i); }
6
+ 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); }
7
+ function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); }
8
+ 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."); }
9
+ 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); }
10
+ function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); }
11
+ function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); }
12
+ 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; }
13
+ import { autoUpdate, computePosition, flip, offset, shift } from '@floating-ui/dom';
2
14
  import { ReactRenderer } from '@tiptap/react';
3
15
  import { EmojiList } from "../component/EmojiList";
4
16
  export var emojiSuggestion = {
@@ -6,40 +18,157 @@ export var emojiSuggestion = {
6
18
  items: function items(_ref) {
7
19
  var editor = _ref.editor,
8
20
  query = _ref.query;
9
- return editor.storage.emoji.emojis.filter(function (_ref2) {
10
- var shortcodes = _ref2.shortcodes,
11
- tags = _ref2.tags;
12
- return shortcodes.find(function (shortcode) {
13
- return shortcode.startsWith(query.toLowerCase());
14
- }) || tags.find(function (tag) {
15
- return tag.startsWith(query.toLowerCase());
21
+ var normalizedQuery = query.toLowerCase().trim();
22
+ if (!normalizedQuery) {
23
+ // 如果没有查询,返回所有 emoji(限制数量以避免性能问题)
24
+ return editor.storage.emoji.emojis.slice(0, 200);
25
+ }
26
+
27
+ // 优化搜索:优先匹配开头,其次匹配包含
28
+ return editor.storage.emoji.emojis.map(function (item) {
29
+ var _item$shortcodes = item.shortcodes,
30
+ shortcodes = _item$shortcodes === void 0 ? [] : _item$shortcodes,
31
+ _item$tags = item.tags,
32
+ tags = _item$tags === void 0 ? [] : _item$tags;
33
+ var score = 0;
34
+
35
+ // 检查 shortcodes 和 tags
36
+ var allMatches = [].concat(_toConsumableArray(shortcodes.map(function (s) {
37
+ return s.toLowerCase();
38
+ })), _toConsumableArray(tags.map(function (t) {
39
+ return t.toLowerCase();
40
+ })));
41
+
42
+ // 计算匹配分数
43
+ var exactStart = allMatches.some(function (m) {
44
+ return m === normalizedQuery;
16
45
  });
17
- });
46
+ var startsWith = allMatches.some(function (m) {
47
+ return m.startsWith(normalizedQuery);
48
+ });
49
+ var includes = allMatches.some(function (m) {
50
+ return m.includes(normalizedQuery);
51
+ });
52
+ if (exactStart) score = 3;else if (startsWith) score = 2;else if (includes) score = 1;else score = 0;
53
+ return {
54
+ item: item,
55
+ score: score
56
+ };
57
+ }).filter(function (_ref2) {
58
+ var score = _ref2.score;
59
+ return score > 0;
60
+ }).sort(function (a, b) {
61
+ return b.score - a.score;
62
+ }) // 按分数排序
63
+ .map(function (_ref3) {
64
+ var item = _ref3.item;
65
+ return item;
66
+ }).slice(0, 100); // 限制结果数量
18
67
  },
19
68
  render: function render() {
20
69
  var component = null;
70
+ var cleanupAutoUpdate = null;
71
+ var isMouseOver = false;
72
+ var shouldClose = false;
73
+ var currentProps = null;
74
+ var updatePosition = function updatePosition(editor, element, clientRect) {
75
+ if (!clientRect) return;
76
+ var virtualElement = {
77
+ getBoundingClientRect: function getBoundingClientRect() {
78
+ return clientRect;
79
+ }
80
+ };
81
+ computePosition(virtualElement, element, {
82
+ placement: 'bottom-start',
83
+ strategy: 'absolute',
84
+ middleware: [offset(4), flip(), shift({
85
+ padding: 8
86
+ })]
87
+ }).then(function (_ref4) {
88
+ var x = _ref4.x,
89
+ y = _ref4.y;
90
+ element.style.position = 'absolute';
91
+ element.style.left = "".concat(x, "px");
92
+ element.style.top = "".concat(y, "px");
93
+ });
94
+ };
21
95
  return {
22
96
  onStart: function onStart(props) {
97
+ var _props$clientRect;
98
+ currentProps = props;
99
+ shouldClose = false;
23
100
  component = new ReactRenderer(EmojiList, {
24
- props: props,
101
+ props: _objectSpread(_objectSpread({}, props), {}, {
102
+ query: props.query || '',
103
+ editor: props.editor,
104
+ command: function command(commandProps) {
105
+ // 标记应该关闭,然后调用原始 command
106
+ shouldClose = true;
107
+ props.command(commandProps);
108
+ }
109
+ }),
25
110
  editor: props.editor
26
111
  });
27
- if (!props.clientRect) return;
112
+ var clientRect = (_props$clientRect = props.clientRect) === null || _props$clientRect === void 0 ? void 0 : _props$clientRect.call(props);
113
+ if (!clientRect) return;
28
114
  var element = component.element;
29
- element.style.position = 'absolute';
30
115
  document.body.appendChild(element);
31
- updatePosition(props.editor, element);
116
+
117
+ // 添加鼠标事件监听
118
+ var handleMouseEnter = function handleMouseEnter() {
119
+ isMouseOver = true;
120
+ };
121
+ var handleMouseLeave = function handleMouseLeave() {
122
+ isMouseOver = false;
123
+ };
124
+ element.addEventListener('mouseenter', handleMouseEnter);
125
+ element.addEventListener('mouseleave', handleMouseLeave);
126
+
127
+ // 初始定位
128
+ updatePosition(props.editor, element, clientRect);
129
+
130
+ // 设置自动更新位置
131
+ var virtualElement = {
132
+ getBoundingClientRect: function getBoundingClientRect() {
133
+ var _currentProps, _currentProps$clientR;
134
+ var rect = (_currentProps = currentProps) === null || _currentProps === void 0 || (_currentProps$clientR = _currentProps.clientRect) === null || _currentProps$clientR === void 0 ? void 0 : _currentProps$clientR.call(_currentProps);
135
+ return rect || new DOMRect();
136
+ }
137
+ };
138
+ cleanupAutoUpdate = autoUpdate(virtualElement, element, function () {
139
+ var _currentProps2, _currentProps2$client;
140
+ var rect = (_currentProps2 = currentProps) === null || _currentProps2 === void 0 || (_currentProps2$client = _currentProps2.clientRect) === null || _currentProps2$client === void 0 ? void 0 : _currentProps2$client.call(_currentProps2);
141
+ if (rect) {
142
+ updatePosition(props.editor, element, rect);
143
+ }
144
+ });
32
145
  },
33
146
  onUpdate: function onUpdate(props) {
147
+ var _props$clientRect2;
148
+ currentProps = props;
34
149
  if (!component) return;
35
- component.updateProps(props);
36
- if (!props.clientRect) return;
37
- updatePosition(props.editor, component.element);
150
+ shouldClose = false;
151
+ component.updateProps(_objectSpread(_objectSpread({}, props), {}, {
152
+ query: props.query || '',
153
+ editor: props.editor,
154
+ command: function command(commandProps) {
155
+ // 标记应该关闭,然后调用原始 command
156
+ shouldClose = true;
157
+ props.command(commandProps);
158
+ }
159
+ }));
160
+ var clientRect = (_props$clientRect2 = props.clientRect) === null || _props$clientRect2 === void 0 ? void 0 : _props$clientRect2.call(props);
161
+ if (!clientRect) return;
162
+ updatePosition(props.editor, component.element, clientRect);
38
163
  },
39
164
  onKeyDown: function onKeyDown(props) {
40
165
  var _component$ref;
41
166
  if (!component) return false;
42
167
  if (props.event.key === 'Escape') {
168
+ if (cleanupAutoUpdate) {
169
+ cleanupAutoUpdate();
170
+ cleanupAutoUpdate = null;
171
+ }
43
172
  component.element.remove();
44
173
  component.destroy();
45
174
  return true;
@@ -48,6 +177,26 @@ export var emojiSuggestion = {
48
177
  },
49
178
  onExit: function onExit() {
50
179
  if (!component) return;
180
+
181
+ // 如果用户已经选择了 emoji,应该关闭
182
+ if (shouldClose) {
183
+ if (cleanupAutoUpdate) {
184
+ cleanupAutoUpdate();
185
+ cleanupAutoUpdate = null;
186
+ }
187
+ component.destroy();
188
+ component.element.remove();
189
+ return;
190
+ }
191
+
192
+ // 如果鼠标在弹框内,不关闭
193
+ if (isMouseOver) {
194
+ return;
195
+ }
196
+ if (cleanupAutoUpdate) {
197
+ cleanupAutoUpdate();
198
+ cleanupAutoUpdate = null;
199
+ }
51
200
  component.destroy();
52
201
  component.element.remove();
53
202
  }
@@ -21,7 +21,8 @@ export interface MenuItem {
21
21
  }
22
22
  export interface MenuProps {
23
23
  id?: string;
24
- width?: number;
24
+ width?: React.CSSProperties['width'];
25
+ maxHeight?: React.CSSProperties['maxHeight'];
25
26
  arrowIcon?: React.ReactNode;
26
27
  list: MenuItem[];
27
28
  header?: React.ReactNode;
@@ -5,10 +5,12 @@ export * from './linewiseConvert';
5
5
  export * from './migrateMathStrings';
6
6
  export * from './resourceExtractor';
7
7
  export * from './shortcutKey';
8
+ import { Node } from '@tiptap/pm/model';
8
9
  import { EditorState } from '@tiptap/pm/state';
9
10
  import { Editor } from '@tiptap/react';
10
11
  export declare const formatFileSize: (bytes: number) => string;
11
12
  export declare const insertNodeAfterPosition: (editor: Editor, pos: number, nodeContent: any) => void;
13
+ export declare const hasMarksInBlock: (node: Node | null | undefined) => boolean;
12
14
  export declare const hasMarksInSelection: (state: EditorState) => boolean;
13
15
  export declare function addOpacityToColor(color: string, opacity: number): string;
14
16
  export declare const getLinkTitle: (href: string) => string;
@@ -15,6 +15,16 @@ export var formatFileSize = function formatFileSize(bytes) {
15
15
  export var insertNodeAfterPosition = function insertNodeAfterPosition(editor, pos, nodeContent) {
16
16
  editor.chain().focus().insertContentAt(pos, nodeContent).run();
17
17
  };
18
+ export var hasMarksInBlock = function hasMarksInBlock(node) {
19
+ var _content;
20
+ if (!node) return false;
21
+ if (node.marks && node.marks.length > 0) return true;
22
+ var children = (_content = node.content) === null || _content === void 0 ? void 0 : _content.content;
23
+ if (!children || children.length === 0) return false;
24
+ return children.some(function (child) {
25
+ return hasMarksInBlock(child);
26
+ });
27
+ };
18
28
  export var hasMarksInSelection = function hasMarksInSelection(state) {
19
29
  if (state.selection.empty) {
20
30
  return false;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ctzhian/tiptap",
3
- "version": "1.12.21",
3
+ "version": "1.12.23",
4
4
  "description": "基于 Tiptap 二次开发的编辑器组件",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.js",