UNPKG

md-editor-v3

Version:

Markdown editor for vue3, developed in jsx and typescript, dark theme、beautify content by prettier、render articles directly、paste or clip the picture and upload it...

474 lines (473 loc) 14.2 kB
import { defineComponent as z, inject as q, ref as D, watch as J, onMounted as K, createVNode as M, reactive as X, shallowRef as G, provide as V, computed as Y, onBeforeUnmount as Z, Fragment as ee } from "vue"; import { p as O } from "./config.mjs"; import { b as $, f as j, G as U, P as te, S as le } from "./event-bus.mjs"; import { g as ne } from "./index5.mjs"; import { createSmoothScroll as oe, debounce as ie } from "@vavt/util"; const ce = `.${O}-preview > [data-line]`, w = (e, l) => +getComputedStyle(e).getPropertyValue(l).replace("px", ""), he = (e, l) => { const I = ie(() => { e.removeEventListener("scroll", o), e.addEventListener("scroll", o), l.removeEventListener("scroll", o), l.addEventListener("scroll", o); }, 50), o = (k) => { const d = e.clientHeight, y = l.clientHeight, m = e.scrollHeight, a = l.scrollHeight, s = (m - d) / (a - y); k.target === e ? (l.removeEventListener("scroll", o), l.scrollTo({ top: e.scrollTop / s // behavior: 'smooth' }), I()) : (e.removeEventListener("scroll", o), e.scrollTo({ top: l.scrollTop * s // behavior: 'smooth' }), I()); }; return [ () => { I().finally(() => { e.dispatchEvent(new Event("scroll")); }); }, () => { e.removeEventListener("scroll", o), l.removeEventListener("scroll", o); } ]; }, ge = (e, l, I) => { const { view: o } = I, k = oe(), d = (c) => o.lineBlockAt(o.state.doc.line(c + 1).from).top, y = (c) => o.lineBlockAt(o.state.doc.line(c + 1).from).bottom; let m = [], a = [], s = []; const L = () => { m = [], a = Array.from( l.querySelectorAll(ce) ), s = a.map((n) => Number(n.dataset.line)); const c = [...s], { lines: v } = o.state.doc; c[0] !== 0 && c.unshift(0); let H = c.shift() || 0, t = c.shift() || v; for (let n = 0; n < v; n++) n === t && (H = n, t = c.shift() || v), m.push({ start: H, end: t - 1 }); }, R = (c, v) => { let H = 1; for (let t = a.length - 1; t - 1 >= 0; t--) { const n = a[t]; if (n.offsetTop <= v) { H = Number(n.dataset.line); break; } } for (let t = m.length - 1; t >= 0; t--) if (d(m[t].start) <= c) { H = H < m[t].start ? H : m[t].start; break; } return H; }; let x = 0, B = 0; const _ = () => { if (B !== 0) return !1; x++; const { scrollDOM: c } = o; let v = w(l, "padding-block-start"); const H = o.lineBlockAtHeight(c.scrollTop), { number: t } = o.state.doc.lineAt(H.from), n = m[t - 1]; if (!n) return !1; let E = 1; const f = l.querySelector(`[data-line="${n.start}"]`) || l.firstElementChild?.firstElementChild, u = l.querySelector(`[data-line="${n.end + 1}"]`) || l.lastElementChild?.lastElementChild, h = c.scrollHeight - c.clientHeight, i = l.scrollHeight - l.clientHeight; let g = d(n.start), p = y(n.end), r = f.offsetTop, b = u.offsetTop - r; g === 0 && (r = 0, f === u ? (v = 0, b = 0) : b = u.offsetTop), E = (c.scrollTop - g) / (p - g); const A = u == l.lastElementChild?.lastElementChild ? u.offsetTop + u.clientHeight : u.offsetTop; if (p >= h || A > i) { const S = R(h, i); g = d(S), E = (c.scrollTop - g) / (h - g); const T = l.querySelector(`[data-line="${S}"]`); g > 0 && T && (r = T.offsetTop), b = i - r + w(l, "padding-block-start"); } const F = r - v + b * E; k(l, F, () => { x--; }); }, P = () => { if (x !== 0) return; B++; const { scrollDOM: c } = o, v = l.scrollTop, H = l.scrollHeight, t = c.scrollHeight - c.clientHeight, n = l.scrollHeight - l.clientHeight; let E = l.firstElementChild?.firstElementChild, f = l.firstElementChild?.lastElementChild; if (s.length > 0) { let S = Math.ceil( s[s.length - 1] * (v / H) ), T = s.findLastIndex((C) => C <= S); T = T === -1 ? 0 : T, S = s[T]; for (let C = T; C >= 0 && C < s.length; ) if (a[C].offsetTop > v) { if (C - 1 >= 0) { C--; continue; } S = -1, T = -1; break; } else { if (C + 1 < s.length && a[C + 1].offsetTop < v) { C++; continue; } S = s[C], T = C; break; } switch (T) { case -1: { E = l.firstElementChild?.firstElementChild, f = a[0]; break; } case s.length - 1: { E = a[T], f = l.firstElementChild?.lastElementChild; break; } default: E = a[T], f = a[T + 1]; } } let u = E === l.firstElementChild?.firstElementChild ? 0 : E.offsetTop - w(E, "margin-block-start"), h = f.offsetTop, i = 0; const { start: g, end: p } = m[Number(E.dataset.line || 0)]; let r = d(g); const b = d( p + 1 === o.state.doc.lines ? p : p + 1 ); let A = 0; const F = f == l.lastElementChild?.lastElementChild ? f.offsetTop + f.offsetHeight : f.offsetTop; if (b > t || F > n) { const S = R(t, n), T = l.querySelector(`[data-line="${S}"]`); u = T ? T.offsetTop - w(T, "margin-block-start") : u, r = d(S), i = (v - u) / (n - u), A = t - r; } else E === l.firstElementChild?.firstElementChild ? (E === f && (h = f.offsetTop + f.offsetHeight + w(f, "margin-block-end")), A = b, r = 0, i = Math.max(v / h, 0)) : (i = Math.max( (v - u) / (h - u), 0 ), A = b - r); k(e, r + A * i, () => { B--; }); }, N = (c) => { const { scrollDOM: v, contentHeight: H } = o, t = v.clientHeight; if (H <= t || l.firstElementChild.clientHeight <= l.clientHeight || o.state.doc.lines <= m[m.length - 1]?.end) return !1; c.target === e ? _() : P(); }; return [ () => { L(), e.addEventListener("scroll", N), l.addEventListener("scroll", N), e.dispatchEvent(new Event("scroll")); }, () => { e.removeEventListener("scroll", N), l.removeEventListener("scroll", N); } ]; }, se = { tocItem: { type: Object, default: () => ({}) }, mdHeadingId: { type: Function, default: () => { } }, onActive: { type: Function, default: () => { } }, onClick: { type: Function, default: () => { } }, scrollElementOffsetTop: { type: Number, default: 0 } }, Q = /* @__PURE__ */ z({ props: se, setup(e) { const l = q("scrollElementRef"), I = q("roorNodeRef"), o = D(); J(() => e.tocItem.active, (d) => { d && e.onActive(e.tocItem, o.value); }), K(() => { e.tocItem.active && e.onActive(e.tocItem, o.value); }); const k = (d) => { if (d.stopPropagation(), e.onClick(d, e.tocItem), d.defaultPrevented) return; const y = e.mdHeadingId({ text: e.tocItem.text, level: e.tocItem.level, index: e.tocItem.index, currentToken: e.tocItem.currentToken, nextToken: e.tocItem.nextToken }), m = I.value.getElementById(y), a = l.value; if (m && a) { let s = m.offsetParent, L = m.offsetTop; if (a.contains(s)) for (; s && a != s; ) L += s?.offsetTop, s = s?.offsetParent; const R = m.previousElementSibling; let x = 0; R || (x = w(m, "margin-block-start")), a?.scrollTo({ top: L - e.scrollElementOffsetTop - x, behavior: "smooth" }); } }; return () => M("div", { ref: o, class: [`${O}-catalog-link`, e.tocItem.active && `${O}-catalog-active`], onClick: k }, [M("span", { title: e.tocItem.text }, [e.tocItem.text]), e.tocItem.children && e.tocItem.children.length > 0 && M("div", { class: `${O}-catalog-wrapper` }, [e.tocItem.children.map((d) => M(Q, { mdHeadingId: e.mdHeadingId, key: `${e.tocItem.text}-link-${d.level}-${d.text}`, tocItem: d, onActive: e.onActive, onClick: e.onClick, scrollElementOffsetTop: e.scrollElementOffsetTop }, null))])]); } }), re = { /** * 编辑器的Id,务必与需要绑定的编辑器Id相同 */ editorId: { type: String, default: void 0 }, class: { type: String, default: "" }, mdHeadingId: { type: Function, default: ({ text: e }) => e }, /** * 指定滚动的容器,选择器需带上对应的符号,默认预览框 * 元素必须定位!!!!!! * * 默认:#md-editor-preview-wrapper */ scrollElement: { type: [String, Object], default: void 0 }, theme: { type: String, default: "light" }, /** * 高亮标题相对滚动容器顶部偏移量,即距离该值时,高亮当前目录菜单项 * * 默认:20px */ offsetTop: { type: Number, default: 20 }, /** * 滚动区域的固定顶部高度 * * 默认:0 */ scrollElementOffsetTop: { type: Number, default: 0 }, onClick: { type: Function, default: void 0 }, onActive: { type: Function, default: void 0 }, /** * 滚动容器是否在web component中,默认不在 * * 在其中的话通过document查询不到 */ isScrollElementInShadow: { type: Boolean, default: !1 }, /** * 设置与哪个区域同步,默认与内容区域同步 * * >= v5.3.0 */ syncWith: { type: String, default: "preview" }, /** * 控制最大显示的目录层级 */ catalogMaxDepth: { type: Number, default: void 0 } }, W = /* @__PURE__ */ z({ name: "MdCatalog", props: re, emits: ["onClick", "onActive"], setup(e, l) { const I = e.editorId, o = `#${I}-preview-wrapper`, k = X({ list: [], show: !1, scrollElement: e.scrollElement || o }), d = G(), y = D(), m = D(), a = D(), s = D(), L = G(), R = D({}); V("scrollElementRef", m), V("roorNodeRef", s); const x = Y(() => { const t = []; return k.list.forEach((n, E) => { if (e.catalogMaxDepth && n.level > e.catalogMaxDepth) return; const { text: f, level: u, line: h } = n, i = { level: u, text: f, line: h, index: E + 1, active: d.value === n }; if (t.length === 0) t.push(i); else { let g = t[t.length - 1]; if (i.level > g.level) for (let p = g.level + 1; p <= 6; p++) { const { children: r } = g; if (!r) { g.children = [i]; break; } if (g = r[r.length - 1], i.level <= g.level) { r.push(i); break; } } else t.push(i); } }), t; }), B = () => { if (k.scrollElement instanceof HTMLElement) return k.scrollElement; let t = document; return (k.scrollElement === o || e.isScrollElementInShadow) && (t = y.value?.getRootNode()), t.querySelector(k.scrollElement); }, _ = (t) => { if (t.length === 0) return d.value = void 0, k.list = [], !1; const { activeHead: n, activeIndex: E } = t.reduce((h, i, g) => { let p = 0; if (e.syncWith === "preview") { const r = s.value?.getElementById(e.mdHeadingId({ text: i.text, level: i.level, index: g + 1, currentToken: i.currentToken, nextToken: i.nextToken })); r instanceof HTMLElement && (p = ne(r, m.value)); } else { const r = L.value; if (r) { const b = r.lineBlockAt(r.state.doc.line(i.line + 1).from).top, A = r.scrollDOM.scrollTop; p = b - A; } } return p < e.offsetTop && p > h.minTop ? { activeHead: i, activeIndex: g, minTop: p } : h; }, { activeHead: t[0], activeIndex: 0, minTop: Number.MIN_SAFE_INTEGER }); let f = n; const { catalogMaxDepth: u } = e; if (u && f.level > u) { for (let h = E; h >= 0; h--) { const i = t[h]; if (i.level <= u) { f = i; break; } } if (f.level > u) { const h = t.find((i) => i.level <= u); h && (f = h); } } d.value = f, k.list = t; }, P = (t, n) => { R.value.top = n.offsetTop + w(n, "padding-block-start") + "px", e.onActive?.(t, n), l.emit("onActive", t, n); }, N = () => { _(k.list); }, c = (t) => { if (a.value?.removeEventListener("scroll", N), e.syncWith === "editor") a.value = L.value?.scrollDOM; else { const n = B(); m.value = n, a.value = n === document.documentElement ? document : n; } _(t), a.value?.addEventListener("scroll", N); }, v = (t) => { L.value = t; }; J([() => e.syncWith, L, () => e.catalogMaxDepth], () => { c(k.list); }), K(() => { s.value = y.value.getRootNode(), $.on(I, { name: j, callback: c }), $.on(I, { name: U, callback: v }), $.emit(I, te), $.emit(I, le); }), Z(() => { $.remove(I, j, c), $.remove(I, U, v), a.value?.removeEventListener("scroll", N); }); const H = (t, n) => { e.onClick?.(t, n), l.emit("onClick", t, n); }; return () => M("div", { class: [`${O}-catalog`, e.theme === "dark" && `${O}-catalog-dark`, e.class || ""], ref: y }, [x.value.length > 0 && M(ee, null, [M("div", { class: `${O}-catalog-indicator`, style: R.value }, null), M("div", { class: `${O}-catalog-container` }, [x.value.map((t) => M(Q, { mdHeadingId: e.mdHeadingId, tocItem: t, key: `link-${t.level}-${t.text}`, onActive: P, onClick: H, scrollElementOffsetTop: e.scrollElementOffsetTop }, null))])])]); } }); W.install = (e) => (e.component(W.name, W), e); export { W as M, he as a, ge as s };