UNPKG

@leoyin/fluent-editor

Version:

A rich text editor based on Quill 2.0, which extends rich modules and formats on the basis of Quill. It's powerful and out-of-the-box.

176 lines (175 loc) 5.8 kB
"use strict"; var __defProp = Object.defineProperty; var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value); Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" }); const native = require("../node_modules/.pnpm/@emoji-mart_data@1.2.1/node_modules/@emoji-mart/data/sets/15/native.json.cjs.js"); const floatingUi_dom = require("../node_modules/.pnpm/@floating-ui_dom@1.7.2/node_modules/@floating-ui/dom/dist/floating-ui.dom.cjs.js"); const module$1 = require("../node_modules/.pnpm/emoji-mart@5.6.0/node_modules/emoji-mart/dist/module.cjs.js"); const lodashEs = require("lodash-es"); const DEFAULT_OPTIONS = { theme: "light", set: "native", skinTonePosition: "none", previewPosition: "bottom", searchPosition: "sticky", categories: ["frequent", "people", "nature", "foods", "activity", "places", "objects", "symbols", "flags"], maxFrequentRows: 2, perLine: 8, navPosition: "top", noCountryFlags: false, dynamicWidth: false }; const PICKER_DOM_ID = "emoji-picker"; const LOCALE_MAP = { "zh-CN": "zh", "en-US": "en" }; class EmojiModule { constructor(quill, options = {}) { __publicField(this, "quill"); __publicField(this, "options"); __publicField(this, "picker", null); __publicField(this, "isPickerVisible", false); __publicField(this, "cleanupResizeObserver", null); this.quill = quill; this.options = options; const toolbar = this.quill.getModule("toolbar"); if (toolbar) { toolbar.addHandler("emoji", () => { if (this.isPickerVisible) { this.closeDialog(); } else { this.openDialog(); } }); } } getEmojiButton() { return document.querySelector(".ql-emoji"); } async updatePickerPosition() { const button = this.getEmojiButton(); const pickerElement = document.getElementById(PICKER_DOM_ID); if (!button || !this.picker || !pickerElement) { return; } try { const { x, y } = await floatingUi_dom.computePosition(button, pickerElement); this.picker.style.top = `${y}px`; this.picker.style.left = `${x}px`; } catch (error) { console.warn("Failed to compute picker position:", error); } } // 监听容器大小变化,更新表情选择弹窗位置 setupContainerResizeObserver() { const container = this.quill.root.parentElement; if (!container) { return null; } const debouncedUpdate = lodashEs.debounce(() => { this.updatePickerPosition(); }, 100); const resizeObserver = new ResizeObserver(() => { debouncedUpdate(); }); resizeObserver.observe(container); return () => { resizeObserver.disconnect(); debouncedUpdate.cancel(); }; } // 创建表情选择弹窗 createPicker() { const pickerConfig = { ...DEFAULT_OPTIONS, // emoji-mart 与 tiny-editor 国际化的的 locale 不一致使用 LOCALE_MAP 转换 locale: LOCALE_MAP[this.quill.lang] ?? "en", ...this.options, data: native.default, onEmojiSelect: this.handleEmojiSelect.bind(this), onClickOutside: this.handleClickOutside.bind(this) }; const picker = new module$1.Picker(pickerConfig); picker.id = PICKER_DOM_ID; picker.style.position = "absolute"; picker.style.zIndex = "1000"; return picker; } // 打开表情弹窗 openDialog() { if (this.picker) { return; } try { this.picker = this.createPicker(); document.body.appendChild(this.picker); this.updatePickerPosition(); this.cleanupResizeObserver = this.setupContainerResizeObserver(); this.isPickerVisible = true; } catch (error) { console.error("Failed to open emoji picker:", error); this.closeDialog(); } } // 关闭表情弹窗 closeDialog() { var _a; if (!this.picker) { return; } this.isPickerVisible = false; this.picker.remove(); this.picker = null; (_a = this.cleanupResizeObserver) == null ? void 0 : _a.call(this); this.cleanupResizeObserver = null; } // 处理表情选择事件 handleEmojiSelect(emoji) { const selection = this.quill.getSelection(true); if (!selection) { console.warn("No selection available for emoji insertion"); return; } try { const emojiDelta = this.quill.insertText(selection.index, emoji.native, "user"); this.closeDialog(); this.setSelectionAfterEmoji(emojiDelta); } catch (error) { console.error("Failed to insert emoji:", error); } } // 设置表情插入后的光标位置 setSelectionAfterEmoji(emojiDelta) { setTimeout(() => { try { const newSelection = this.quill.getSelection(true); if (newSelection && emojiDelta) { this.quill.setSelection(newSelection.index + emojiDelta.length()); } } catch (error) { console.warn("Failed to set selection after emoji insertion:", error); } }, 0); } // 处理外部点击事件 handleClickOutside(event) { const button = this.getEmojiButton(); const target = event.target; const isClickOnButton = target === button || target instanceof Element && (button == null ? void 0 : button.contains(target)); if (isClickOnButton) { return; } this.closeDialog(); } // 销毁模块,清理资源 destroy() { var _a; (_a = this.cleanupResizeObserver) == null ? void 0 : _a.call(this); this.cleanupResizeObserver = null; this.closeDialog(); } } exports.EmojiModule = EmojiModule; //# sourceMappingURL=emoji.cjs.js.map