@matechat/core
Version:
前端智能化场景解决方案UI库,轻松构建你的AI应用。
427 lines (426 loc) • 13.1 kB
JavaScript
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} ${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
};