UNPKG

@opentiny/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.

168 lines (167 loc) 5.3 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 debounce = require("../utils/debounce.cjs.js"); const DEFAULT_OPTIONS = { // @ts-ignore emojiData: window.emojiData, // @ts-ignore EmojiPicker: window.EmojiPicker, // @ts-ignore emojiPickerPosition: window.emojiPickerPosition, 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 = { ...DEFAULT_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; } const { emojiPickerPosition } = this.options; try { const { x, y } = await emojiPickerPosition(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 = debounce.debounce(() => { this.updatePickerPosition(); }, 100); const resizeObserver = new ResizeObserver(() => { debouncedUpdate(); }); resizeObserver.observe(container); return () => { resizeObserver.disconnect(); debouncedUpdate.cancel(); }; } // 创建表情选择弹窗 createPicker() { const { EmojiPicker, emojiData, ...options } = this.options; const pickerConfig = { // emoji-mart 与 tiny-editor 国际化的的 locale 不一致使用 LOCALE_MAP 转换 locale: LOCALE_MAP[this.quill.lang] ?? "en", data: emojiData, ...options, onEmojiSelect: this.handleEmojiSelect.bind(this), onClickOutside: this.handleClickOutside.bind(this) }; const picker = new EmojiPicker(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 insertIndex = selection.index; this.quill.insertText(insertIndex, emoji.native, "user"); this.closeDialog(); this.quill.setSelection(insertIndex + emoji.native.length); } catch (error) { console.error("Failed to insert emoji:", error); } } // 处理外部点击事件 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