sql-editor-react
Version:
基于monaco-editor react 实现的sql编辑器,支持语法高亮、 关联数据库,表名 关键字 功能
458 lines (457 loc) • 12 kB
JavaScript
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
};