UNPKG

@matechat/core

Version:

前端智能化场景解决方案UI库,轻松构建你的AI应用。

427 lines (426 loc) 13.1 kB
import "./index.css"; var O = Object.defineProperty; var T = (s, e, t) => e in s ? O(s, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : s[e] = t; var u = (s, e, t) => T(s, typeof e != "symbol" ? e + "" : e, t); import { ref as E, reactive as b, nextTick as v, watch as m, onMounted as A, onUnmounted as $, defineComponent as U, openBlock as y, createElementBlock as C, Fragment as D, createVNode as x, unref as w, withCtx as I, renderSlot as k, createBlock as R, Teleport as L, Transition as B, normalizeStyle as H, normalizeClass as W, createCommentVNode as K } from "vue"; import { PopperTrigger as z } from "@matechat/core/PopperTrigger"; import { computePosition as S, offset as V, autoUpdate as F } from "@floating-ui/dom"; import { debounce as j, isObject as N } from "lodash-es"; const q = { modelValue: { type: Boolean, default: !1 }, prefix: { type: Array, default: () => [] }, fitHostWidth: { type: Boolean, default: !0 }, optionsCount: { type: Number, default: 0 }, menuClass: { type: String } }, G = ["update:modelValue", "searchChange", "toggleChange"], p = () => { }; class f { constructor(e) { u(this, "_adapter"); this._adapter = { ...f.defaultAdapter, ...e }; } static get cssClasses() { return {}; } static get strings() { return {}; } static get numbers() { return {}; } static get defaultAdapter() { return { getProp: p, getProps: p, getState: p, getStates: p, setState: p, getContext: p, getContexts: p, getCache: p, setCache: p, getCaches: p, stopPropagation: p }; } getProp(e) { return this._adapter.getProp(e); } getProps() { return this._adapter.getProps(); } getState(e) { return this._adapter.getState(e); } getStates() { return this._adapter.getStates(); } setState(e, t) { return this._adapter.setState({ ...e }, t); } /* istanbul ignore next */ getCaches() { return this._adapter.getCaches(); } getCache(e) { return this._adapter.getCache(e); } setCache(e, t) { return e && this._adapter.setCache(e, t); } nextTick(e) { return this._adapter.nextTick(e); } _isInProps(e) { const t = this.getProps(); return e in t; } init(e) { } destroy() { } } const g = " ", J = "ArrowLeft", Q = "ArrowRight", X = "ArrowUp", Y = "ArrowDown", Z = "Escape"; class tt extends f { constructor(t) { super({ ...t }); u(this, "props"); u(this, "inputEl", null); u(this, "originEl", null); u(this, "overlayEl", null); u(this, "currentTrigger", ""); u(this, "currentSearchValue", ""); u(this, "currentTriggerIndex", -1); u(this, "currentCursorIndex", -1); u(this, "autoUpdateCleanup", null); // 处理输入事件 u(this, "handleInput", () => { if (!this.inputEl) return; const t = this.inputEl.value, i = this.inputEl.selectionStart || 0, o = this.checkPrefixMatch(t, i); if (o) { const { trigger: l, triggerIndex: a, searchValue: n } = o; this.currentTrigger = l, this.currentSearchValue = n, this.currentTriggerIndex = a, this.currentCursorIndex = i, this._adapter.searchChange({ value: n, trigger: l, triggerIndex: a, cursorIndex: i }), this.updateModelValue(!0); } else this.resetMention(); }); // 处理键盘事件 u(this, "handleKeyDown", (t) => { if (this.inputEl) switch (t.key) { case X: this.props.modelValue && (t.preventDefault(), this.handleArrowKey(-1)); break; case Y: this.props.modelValue && (t.preventDefault(), this.handleArrowKey(1)); break; case J: case Q: setTimeout(() => { this.handleInput(); }); break; case Z: t.preventDefault(), this.resetMention(); break; case g: this.props.modelValue && this.resetMention(); break; } }); // 处理点击事件 u(this, "handleClick", () => { this.inputEl && this.handleInput(); }); // 处理文档点击事件 u(this, "handleDocumentClick", (t) => { this.originEl && (this.props.modelValue ? this.originEl.contains(t.target) || this.resetMention() : this.originEl.contains(t.target) && this.handleInput()); }); this.props = { modelValue: !1, prefix: [], fitHostWidth: !0, optionsCount: 0, ...this.getProps() }, this.handleInput = j(this.handleInput, 300); } // 更新配置选项 updateOptions(t) { this.props = { ...this.props, ...t }, this.updateOverlayPosition(); } // 设置输入元素 setInputEl(t) { this.inputEl = t; } // 设置原点元素 setOriginEl(t) { this.originEl = t, this.updateOverlayPosition(); } // 设置覆盖层元素 setOverlayEl(t) { this.overlayEl = t, this.updateOverlayPosition(); } // 初始化事件 initEvents() { this.inputEl && (this.inputEl.addEventListener("input", this.handleInput), this.inputEl.addEventListener("keydown", this.handleKeyDown), this.inputEl.addEventListener("click", this.handleClick)), document.addEventListener("click", this.handleDocumentClick); } // 销毁事件 destroy() { this.inputEl && (this.inputEl.removeEventListener("input", this.handleInput), this.inputEl.removeEventListener("keydown", this.handleKeyDown), this.inputEl.removeEventListener("click", this.handleClick)), document.removeEventListener("click", this.handleDocumentClick), this.autoUpdateCleanup && (this.autoUpdateCleanup(), this.autoUpdateCleanup = null), this.inputEl = null, this.originEl = null, this.overlayEl = null; } // 重置Mention状态 resetMention() { this.currentTrigger = "", this.currentSearchValue = "", this.currentTriggerIndex = -1, this.currentCursorIndex = -1, this.updateModelValue(!1); } // 更新模型值 updateModelValue(t) { this.props.modelValue !== t && (this.props.modelValue = t, this._adapter.updateModelValue(t), this._adapter.toggleChange(t)); } // 检查前缀匹配 checkPrefixMatch(t, i) { if (!this.props.prefix || this.props.prefix.length === 0) return null; const l = t.replace(/[\r\n]/g, g).substring(0, i); for (const a of this.props.prefix) { let n = "", d = !1; if (typeof a == "string") n = a; else if (N(a)) n = a.key, d = !!a.onlyInputStart; else continue; const h = l.lastIndexOf(n), r = l.lastIndexOf(g), c = l.substring(h, i), P = c.charAt(c.length - 1); if (!(h < 0 || h > 0 && d || h < r || P === g)) { const M = c.slice(n.length); return { trigger: n, triggerIndex: h, searchValue: M }; } } return null; } // 处理箭头键 handleArrowKey(t) { let i = (this.getCurrentActiveIndex() + t) % this.props.optionsCount; i < 0 && (i = this.props.optionsCount - 1), this._adapter.activeIndexChange(i); } // 获取当前活动索引 getCurrentActiveIndex() { return 0; } // 处理选择事件 handleSelect() { if (!this.inputEl || this.currentTriggerIndex === -1) return; const t = this.inputEl.value, i = t.substring(0, this.currentTriggerIndex), o = t.substring(this.currentCursorIndex), l = `${i}@selected ${o}`; this.inputEl.value = l; const a = i.length + 10; this.inputEl.setSelectionRange(a, a), this.resetMention(), this.inputEl.dispatchEvent(new Event("input", { bubbles: !0 })); } // 更新覆盖层位置 updateOverlayPosition() { return new Promise((t) => { if (!this.originEl || !this.overlayEl) { t(void 0); return; } if (this.props.fitHostWidth) { const i = this.originEl.getBoundingClientRect(); this.overlayEl.style.width = `${i.width}px`; } else this.overlayEl.style.width = ""; this.autoUpdateCleanup && (this.autoUpdateCleanup(), this.autoUpdateCleanup = null), S(this.originEl, this.overlayEl, { strategy: "fixed", placement: "top-start", middleware: [V(4)] }).then(({ x: i, y: o }) => { this.overlayEl.style.top = `${o}px`, this.overlayEl.style.left = `${i}px`, this.overlayEl.style.position = "fixed", t({ top: `${o}px`, left: `${i}px`, width: this.props.fitHostWidth ? `${this.originEl.getBoundingClientRect().width}px` : "" }), this.autoUpdateCleanup = F( this.originEl, this.overlayEl, () => { if (this.originEl && this.overlayEl) { if (this.props.fitHostWidth) { const l = this.originEl.getBoundingClientRect(); this.overlayEl.style.width = `${l.width}px`; } S(this.originEl, this.overlayEl, { strategy: "fixed", placement: "top-start", middleware: [V(4)] }).then(({ x: l, y: a }) => { this.overlayEl.style.top = `${a}px`, this.overlayEl.style.left = `${l}px`; }); } }, { // 添加对视口变化的更全面监听,确保浏览器F12打开/关闭时位置能正确更新 animationFrame: !0, elementResize: !0, ancestorScroll: !0, ancestorResize: !0, layoutShift: !0 } ); }).catch(() => { t(void 0); }); }); } // 获取当前搜索值 getCurrentSearchValue() { return this.currentSearchValue; } // 获取当前触发词 getCurrentTrigger() { return this.currentTrigger; } } function et(s, e) { const t = E(), i = E(), o = E(), l = b({ top: "0px", left: "0px", width: "" }); let a, n; n = new tt({ ...f.defaultAdapter, getProps: () => ({ modelValue: s.modelValue, prefix: s.prefix, fitHostWidth: s.fitHostWidth, optionsCount: s.optionsCount }), nextTick: (r) => v(r), // 使用Vue的nextTick函数确保DOM更新完成后执行回调 updateModelValue: (r) => e("update:modelValue", r), searchChange: (r) => e("searchChange", r), activeIndexChange: () => { }, toggleChange: (r) => e("toggleChange", r) }), m( () => s.modelValue, (r, c) => { n.updateOptions({ modelValue: r }), c !== void 0 && e("toggleChange", r), r && v(() => { o.value && n.setOverlayEl(o.value), h(); }); }, { immediate: !0 } ), m( () => s.prefix, (r) => { n.updateOptions({ prefix: r }); }, { deep: !0 } ), m( () => s.fitHostWidth, (r) => { n.updateOptions({ fitHostWidth: r }); } ); const d = () => { i.value && (a = i.value.querySelector("textarea") || i.value.querySelector("input"), a && (n.setInputEl(a), n.setOriginEl(i.value), o.value && n.setOverlayEl(o.value), n.initEvents())); }, h = () => { if (i.value && o.value) { const r = n.updateOverlayPosition(); r instanceof Promise ? r.then((c) => { c && Object.assign(l, c); }) : r && Object.assign(l, r); } }; return A(() => { const r = t.value.triggerEl; i.value = r.$el ?? r, d(); }), $(() => { n.destroy(); }), { popperTriggerEl: t, originEl: i, overlayEl: o, overlayStyle: l, initEvent: d, mentionFoundation: n, resetMention: () => n.resetMention(), updateOptions: (r) => n.updateOptions(r), updatePosition: h }; } const it = /* @__PURE__ */ U({ __name: "Mention", props: q, emits: G, setup(s, { emit: e }) { const t = s, i = e, { popperTriggerEl: o, overlayEl: l, overlayStyle: a } = et(t, i); return (n, d) => (y(), C( D, null, [ x( w(z), { ref_key: "popperTriggerEl", ref: o }, { default: I(() => [ k(n.$slots, "default", {}, void 0, !0) ]), _: 3 /* FORWARDED */ }, 512 /* NEED_PATCH */ ), (y(), R(L, { to: "body" }, [ x(B, { name: "mc-mention-fade" }, { default: I(() => [ n.modelValue ? (y(), C( "div", { key: 0, ref_key: "overlayEl", ref: l, class: W(["mc-mention", n.menuClass]), style: H(w(a)) }, [ k(n.$slots, "menu", {}, void 0, !0) ], 6 /* CLASS, STYLE */ )) : K("v-if", !0) ]), _: 3 /* FORWARDED */ }) ])) ], 64 /* STABLE_FRAGMENT */ )); } }), nt = (s, e) => { const t = s.__vccOpts || s; for (const [i, o] of e) t[i] = o; return t; }, _ = /* @__PURE__ */ nt(it, [["__scopeId", "data-v-8d228337"]]); _.install = (s) => { s.component("McMention", _); }; export { _ as McMention };