UNPKG

sql-editor-react

Version:

基于monaco-editor react 实现的sql编辑器,支持语法高亮、 关联数据库,表名 关键字 功能

458 lines (457 loc) 12 kB
import { l as X, a as oe, j as L, c as le, e as g, m as I } from "./vendor-CFxAdH8v.js"; import ue, { useState as _, useRef as S, useEffect as u, useCallback as ee, useImperativeHandle as ae, useMemo as de } from "react"; import * as B from "monaco-editor"; import { editor as J, languages as fe } from "monaco-editor"; import { format as ge } from "sql-formatter"; import { FullscreenOutlined as pe, FullscreenExitOutlined as me } from "@ant-design/icons"; const { keywords: he } = oe, be = () => he.map((l) => ({ label: l, kind: X.CompletionItemKind.Keyword, insertText: `${l} `, detail: "内置关键字" })), Me = (o) => Object.keys(o).map((s) => ({ label: s, kind: X.CompletionItemKind.Constant, insertText: s, detail: "数据库" })), xe = (o, l) => { const s = o[l]; return s ? s.map((i) => ({ label: i, kind: X.CompletionItemKind.Constant, insertText: i, detail: "表" })) : []; }, te = (o) => ge(o); function k() { } function ne(o) { return /^\d+$/.test(o) ? `${o}px` : o; } const Ee = ({ isFullScreen: o, toggleFullScreen: l, quitFullScreen: s, children: E }) => { const i = `toolbar ${o ? "fullscreen" : ""}`; return /* @__PURE__ */ L.jsxs( "div", { className: i, style: { width: o ? "100vw" : "auto", height: o ? 0 : 30 }, children: [ o ? /* @__PURE__ */ L.jsx( pe, { onClick: s, style: { color: "#000", cursor: "pointer", padding: "5px", position: "fixed", right: "25px", top: "20px", fontSize: 20, zIndex: 100001 } } ) : /* @__PURE__ */ L.jsx( me, { onClick: l, style: { height: "20px", width: "20px" } } ), E ] } ); }, { keywords: ve } = oe; async function Ce(o, l) { const s = new FontFace(o, `local(${o}), url(${l})`); await s.load(), document.fonts.add(s); } const Oe = { selectOnLineNumbers: !0, roundedSelection: !1, cursorStyle: "line", readOnly: !1, fontSize: 16, minimap: { // 小地图配置 enabled: !1 // 禁用小地图 }, glyphMargin: !0 }; function re(o, l) { const { width: s = "100%", // 默认宽度 height: E = "100%", // 默认高度 value: i = "", // 编辑器内容 theme: h = "naruto", // 主题 language: v = "sql", // 语言,默认 SQL autoComplete: N = be, // 默认sql自动补全 options: p = {}, // 编辑器选项 overrideServices: H = {}, editorWillMount: P = k, editorDidMount: A = k, onChange: C = k, defaultValue: O = "", // 默认值 dataBase: b = {}, //数据库表数据 className: V, children: R, // 接收 children ...T } = o; se(); const t = { ...Oe, ...p }, [w, j] = _(O), D = S(), z = S(null), n = S(), [a, F] = _(!1), [q, G] = _(null), e = () => P(B) || {}; u(() => { (async () => { const f = (await import("monaco-editor")).editor; G(f); })(); }, []); const c = () => { n.current && n.current.setValue(te(n.current.getValue())); }, d = (r) => { n.current && n.current.updateOptions({ readOnly: r }); }, M = () => { F(!1); }, x = () => { F((r) => !r); }, Q = ee((r) => { r.code === "Escape" && M(); }, []); ae(l, () => ({ container: D.current || null, editor: n.current, monaco: B, format: c, toggleFullScreen: x, // 暴露 toggleFullScreen 方法 setReadOnly: d })), u(() => { j(i); }, [i]), u(() => { n.current && n.current.getValue() !== w && n.current.setValue(w), p.theme && J.setTheme(p.theme); }, [w, p.theme]), u(() => { const r = n.current; if (r) { const f = (m) => { a && Q(m); }; return r.layout(), window.addEventListener("keyup", f), () => { window.removeEventListener("keyup", f); }; } }, [Q, a]), u(() => () => { n.current && n.current.dispose(), window.removeEventListener("keyup", Q); }, []), u(() => { let r; if (n.current && N) { const f = n.current.getModel(), m = n.current.getPosition(); f && m && (r = fe.registerCompletionItemProvider(v, { triggerCharacters: [".", ...ve], provideCompletionItems: (y, K) => { let W = []; const { lineNumber: Y, column: ce } = K, Z = y.getValueInRange({ startLineNumber: Y, startColumn: 0, endLineNumber: Y, endColumn: ce }).trim().split(/\s+/), $ = Z[Z.length - 1]; if ($ != null && $.endsWith(".")) { const U = $.slice(0, $.length - 1); Object.keys(b).includes(U) && (W = [...xe(b, U)]); } else $ === "." ? W = [] : W = [ ...Me(b), ...N(y, K) ]; return { suggestions: W }; } })); } return () => { r && r.dispose(); }; }, [v, N, b]), u(() => { if (n.current) { const r = n.current.getRawOptions(); Object.keys(r).forEach( (f) => { const m = p[f]; r[f] !== m && m !== void 0 && n.current.updateOptions({ [f]: m }); } ); } }, [p]); const ie = ee( (r) => { if (r !== null && q) { const f = { ...t, ...e() }; D.current = r; const m = B.editor.createModel(te(w), v); n.current = J.create( r, { model: m, ...V ? { extraEditorClassName: V } : {}, ...f, ...h ? { theme: h } : {} }, H ), n.current.addAction({ id: "format.sql", label: "SQL 格式化", contextMenuGroupId: "navigation", contextMenuOrder: 1, run: function() { c(); } }), p.theme && J.setTheme(p.theme), A(n.current, B), n.current.onDidChangeModelContent((y) => { const K = n.current.getValue(); C(K, y); }), Ce("codicon", le).catch((y) => { y && console.error("Failed to load font codicon", y); }); } }, [q] ); return /* @__PURE__ */ L.jsxs( "div", { ref: z, style: { width: a ? "100vw" : s, // 100vw 确保在全屏时宽度为视口宽度 height: a ? "100vh" : E, // 100vh 确保在全屏时高度为视口高度 position: a ? "fixed" : "relative", // 固定定位在全屏时 top: a ? 0 : void 0, left: a ? 0 : void 0, zIndex: a ? 9999 : void 0, // 确保在全屏时覆盖其他内容 marginBottom: 40 }, children: [ /* @__PURE__ */ L.jsx( Ee, { isFullScreen: a, toggleFullScreen: x, quitFullScreen: M, children: R } ), /* @__PURE__ */ L.jsx( "div", { ...T, ref: ie, style: { width: a ? "100vw" : s, height: a ? "100vh" : E } } ) ] } ); } re.displayName = "Editor"; const Te = ue.forwardRef(re), we = { selectOnLineNumbers: !0, roundedSelection: !1, cursorStyle: "line", readOnly: !0, fontSize: 14, scrollbar: { vertical: "auto", // 初始滚动条设置 horizontal: "auto", verticalScrollbarSize: 10, // 设置垂直滚动条的宽度 horizontalScrollbarSize: 10 // 设置水平滚动条的高度 }, minimap: { // 小地图配置 enabled: !1 // 禁用小地图 }, glyphMargin: !0 }, ye = ({ width: o = "100%", height: l = "100%", value: s = "", defaultValue: E = "", language: i = "sql", theme: h = "naruto", options: v = { ...we }, overrideServices: N = {}, editorWillMount: p = k, editorDidMount: H = k, editorWillUnmount: P = k, onChange: A = k, className: C, original: O = "", originalUri: b, modifiedUri: V, renderSideBySide: R = !0 }) => { se(); const T = S(null), t = S(null), w = S(null), j = S(!1), D = ne(o), z = ne(l), n = de( () => ({ width: D, height: z }), [D, z] ), a = () => p(I) || {}, F = () => { if (t.current) { H(t.current, I); const e = t.current.getModel(); if (e) { const { modified: c } = e; w.current = c.onDidChangeContent( (d) => { j.current || A(c.getValue(), d); } ); } } }, q = () => { t.current && P(t.current, I); }, G = () => { if (t.current) { const e = s ?? E, c = b == null ? void 0 : b(I), d = V == null ? void 0 : V(I); let M = c ? g.getModel(c) : null, x = d ? g.getModel(d) : null; M ? (M.setValue(O), g.setModelLanguage(M, i)) : M = g.createModel( O, i, c ), x ? (x.setValue(e), g.setModelLanguage(x, i)) : x = g.createModel( e, i, d ), t.current.setModel({ original: M, modified: x }); } }; return u(() => { if (T.current) { const e = a(); t.current = g.createDiffEditor( T.current, { ...e, ...C ? { extraEditorClassName: C } : {}, ...v, ...h ? { theme: h } : {}, renderSideBySide: R, enableSplitViewResizing: !0 }, N ), G(), F(); } return () => { var e; if (q(), t.current) { t.current.dispose(); const { original: c, modified: d } = t.current.getModel() || {}; c == null || c.dispose(), d == null || d.dispose(); } (e = w.current) == null || e.dispose(); }; }, [R]), u(() => { t.current && t.current.updateOptions({ ...C ? { extraEditorClassName: C } : {}, ...v }); }, [C, v]), u(() => { t.current && t.current.layout(); }, [o, l]), u(() => { if (t.current) { const e = t.current.getModel(); if (e) { const { original: c, modified: d } = e; g.setModelLanguage(c, i), g.setModelLanguage(d, i); } } }, [i]), u(() => { if (t.current) { const { modified: e } = t.current.getModel() || {}; j.current = !0, t.current.getModifiedEditor().pushUndoStop(), e == null || e.pushEditOperations( [], [ { range: e == null ? void 0 : e.getFullModelRange(), text: s } ], () => [] ), t.current.getModifiedEditor().pushUndoStop(), j.current = !1; } }, [s]), u(() => { h && g.setTheme(h); }, [h]), u(() => { if (t.current) { const { original: e } = t.current.getModel() ?? {}; O !== (e == null ? void 0 : e.getValue()) && (e == null || e.setValue(O)); } }, [O]), /* @__PURE__ */ L.jsx( "div", { ref: T, style: n, className: "react-monaco-editor-container" } ); }; ye.displayName = "DiffEditor"; const se = () => { g.defineTheme("naruto", { base: "vs", // 以哪个默认主题为基础:"vs" | "vs-dark" | "hc-black" | "hc-light" inherit: !0, rules: [ // 高亮规则,即给代码里不同token类型的代码设置不同的显示样式 { token: "identifier", foreground: "#d06733" }, { token: "number", foreground: "#6bbeeb", fontStyle: "italic" }, { token: "keyword", foreground: "#05a4d5" } ], colors: { "scrollbarSlider.background": "#bcbcbc", // 滚动条背景 "editorCursor.foreground": "#d4b886", // 焦点颜色 "editor.lineHighlightBackground": "#6492a520", // 焦点所在的一行的背景颜色 "editorLineNumber.foreground": "#008800", // 行号字体颜色 "editorLineNumber.background": "#333333", "editorGutter.background": "#f5f6f7" } }); }; export { ye as DiffEditor, Te as Editor, se as narutoTheme };