@keboola/design 8.0.0 → 9.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/editors.cjs +325 -129
- package/dist/editors.cjs.map +1 -1
- package/dist/editors.d.cts +29 -3
- package/dist/editors.d.ts +29 -3
- package/dist/editors.js +329 -133
- package/dist/editors.js.map +1 -1
- package/dist/rjsf.cjs +317 -20
- package/dist/rjsf.cjs.map +1 -1
- package/dist/rjsf.js +321 -24
- package/dist/rjsf.js.map +1 -1
- package/package.json +5 -5
package/dist/editors.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { createContext,
|
|
2
|
-
import { completeAnyWord, autocompletion } from '@codemirror/autocomplete';
|
|
1
|
+
import { createContext, useRef, useEffect, useLayoutEffect, useMemo, useCallback, useSyncExternalStore, useState, use, Children, Fragment as Fragment$1 } from 'react';
|
|
2
|
+
import { completeFromList, completeAnyWord, autocompletion } from '@codemirror/autocomplete';
|
|
3
3
|
import { jsonParseLinter, json } from '@codemirror/lang-json';
|
|
4
4
|
import { linter } from '@codemirror/lint';
|
|
5
5
|
import { unifiedMergeView } from '@codemirror/merge';
|
|
6
|
-
import {
|
|
6
|
+
import { EditorView, Decoration, ViewPlugin, gutter, GutterMarker, keymap, placeholder, tooltips, gutterLineClass } from '@codemirror/view';
|
|
7
7
|
import CodeMirror, { EditorState, EditorView as EditorView$1, keymap as keymap$1 } from '@uiw/react-codemirror';
|
|
8
8
|
import { clsx } from 'clsx';
|
|
9
9
|
import { extendTailwindMerge } from 'tailwind-merge';
|
|
@@ -21,12 +21,12 @@ import { r } from '@codemirror/legacy-modes/mode/r';
|
|
|
21
21
|
import { shell } from '@codemirror/legacy-modes/mode/shell';
|
|
22
22
|
import { toml } from '@codemirror/legacy-modes/mode/toml';
|
|
23
23
|
import { githubLightInit } from '@uiw/codemirror-theme-github';
|
|
24
|
-
import { sfsql } from '@keboola/codemirror-lang-sfsql';
|
|
24
|
+
import { modeSpec, sfsql } from '@keboola/codemirror-lang-sfsql';
|
|
25
25
|
import colors from '@keboola/tailwind-config/colors';
|
|
26
26
|
import { simpleMode } from '@codemirror/legacy-modes/mode/simple-mode';
|
|
27
|
+
import { StateEffect, StateField, RangeSet, Compartment, RangeSetBuilder } from '@codemirror/state';
|
|
27
28
|
import { defaultKeymap } from '@codemirror/commands';
|
|
28
29
|
import { installExtensions } from '@keboola/codemirror-lang-sql';
|
|
29
|
-
import { StateEffect, StateField, RangeSet, RangeSetBuilder } from '@codemirror/state';
|
|
30
30
|
|
|
31
31
|
// src/components/CodeEditor/CodeEditor.tsx
|
|
32
32
|
|
|
@@ -290,6 +290,21 @@ Tooltip.Trigger = TooltipTrigger;
|
|
|
290
290
|
|
|
291
291
|
// src/utils/escapeStringRegexp.ts
|
|
292
292
|
var escapeStringRegexp = (string) => string.replace(/[|\\{}()[\]^$+*?.]/g, "\\$&").replace(/-/g, "\\x2d");
|
|
293
|
+
|
|
294
|
+
// src/utils/isMac.ts
|
|
295
|
+
var getIsMac = () => {
|
|
296
|
+
if (typeof navigator !== "undefined" && "userAgentData" in navigator) {
|
|
297
|
+
const platform = navigator.userAgentData.platform;
|
|
298
|
+
if (typeof platform === "string") {
|
|
299
|
+
return platform.toLowerCase() === "macos";
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
if (typeof navigator !== "undefined" && typeof navigator.platform === "string") {
|
|
303
|
+
return navigator.platform.toUpperCase().indexOf("MAC") >= 0;
|
|
304
|
+
}
|
|
305
|
+
return false;
|
|
306
|
+
};
|
|
307
|
+
var isMac = getIsMac();
|
|
293
308
|
var helpVariants = cva(["tw:m-0", "tw:text-xs"], {
|
|
294
309
|
defaultVariants: {
|
|
295
310
|
state: "default"
|
|
@@ -740,8 +755,6 @@ var ButtonToolbar = ({ value, children }) => {
|
|
|
740
755
|
};
|
|
741
756
|
var COMMANDS = [
|
|
742
757
|
"run-operation",
|
|
743
|
-
"source freshness",
|
|
744
|
-
"docs generate",
|
|
745
758
|
"run",
|
|
746
759
|
"test",
|
|
747
760
|
"seed",
|
|
@@ -750,9 +763,13 @@ var COMMANDS = [
|
|
|
750
763
|
"ls",
|
|
751
764
|
"compile",
|
|
752
765
|
"build",
|
|
753
|
-
"deps"
|
|
766
|
+
"deps",
|
|
767
|
+
"docs",
|
|
768
|
+
"source"
|
|
754
769
|
];
|
|
770
|
+
var SUBCOMMANDS = { docs: ["generate"], source: ["freshness"] };
|
|
755
771
|
var ARGUMENTS = ["--select", "--exclude", "--selector", "--defer", "--resource-type", "-s"];
|
|
772
|
+
var SELECTORS = ["--select", "--exclude", "-s"];
|
|
756
773
|
var METHODS = [
|
|
757
774
|
"path:",
|
|
758
775
|
"source:",
|
|
@@ -779,13 +796,59 @@ var FLAGS = [
|
|
|
779
796
|
"--profile",
|
|
780
797
|
"--full-refresh"
|
|
781
798
|
];
|
|
782
|
-
var
|
|
783
|
-
|
|
799
|
+
var COMMAND_PHRASES = COMMANDS.flatMap((command) => {
|
|
800
|
+
const subcommands = SUBCOMMANDS[command];
|
|
801
|
+
return subcommands ? subcommands.map((subcommand) => `${command} ${subcommand}`) : [command];
|
|
802
|
+
});
|
|
803
|
+
var alternation = (list) => [...list].sort((a, b) => b.length - a.length).join("|");
|
|
804
|
+
var word = (list) => new RegExp(`^\\s+(?:${alternation(list)})\\b`, "i");
|
|
805
|
+
var prefix = (list) => new RegExp(`^\\s+(?:${alternation(list)})`, "i");
|
|
806
|
+
var WORD = /[\w:-]*$/;
|
|
807
|
+
var asOptions = (list, type) => list.map((label) => ({ label, type }));
|
|
808
|
+
var DBT = [{ label: "dbt", type: "keyword" }];
|
|
809
|
+
var COMMAND_OPTIONS = asOptions(COMMANDS, "keyword");
|
|
810
|
+
var SUBCOMMAND_OPTIONS = Object.fromEntries(
|
|
811
|
+
Object.entries(SUBCOMMANDS).map(([command, subcommands]) => [
|
|
812
|
+
command,
|
|
813
|
+
asOptions(subcommands, "keyword")
|
|
814
|
+
])
|
|
815
|
+
);
|
|
816
|
+
var ARGUMENT_OPTIONS = asOptions(ARGUMENTS, "variable");
|
|
817
|
+
var FLAG_OPTIONS = asOptions(FLAGS, "constant");
|
|
818
|
+
var METHOD_OPTIONS = asOptions(METHODS, "property");
|
|
819
|
+
var optionsAfter = (words) => {
|
|
820
|
+
const [step, command, ...rest] = words;
|
|
821
|
+
if (step === void 0) return DBT;
|
|
822
|
+
if (step.toLowerCase() !== "dbt") return [];
|
|
823
|
+
if (command === void 0) return COMMAND_OPTIONS;
|
|
824
|
+
const subcommands = SUBCOMMAND_OPTIONS[command.toLowerCase()];
|
|
825
|
+
if (subcommands && rest.length === 0) return subcommands;
|
|
826
|
+
const lastArgument = rest.filter((it) => it.startsWith("-")).at(-1);
|
|
827
|
+
const selecting = lastArgument !== void 0 && SELECTORS.includes(lastArgument.toLowerCase());
|
|
828
|
+
return selecting ? [...ARGUMENT_OPTIONS, ...FLAG_OPTIONS, ...METHOD_OPTIONS] : [...ARGUMENT_OPTIONS, ...FLAG_OPTIONS];
|
|
829
|
+
};
|
|
830
|
+
var complete = (context) => {
|
|
831
|
+
const typed = context.matchBefore(WORD);
|
|
832
|
+
if (!typed?.text && !context.explicit) return null;
|
|
833
|
+
const from = typed?.from ?? context.pos;
|
|
834
|
+
const line = context.state.doc.lineAt(from);
|
|
835
|
+
const options = optionsAfter(
|
|
836
|
+
line.text.slice(0, from - line.from).split(/\s+/).filter(Boolean)
|
|
837
|
+
);
|
|
838
|
+
return options.length > 0 ? { from, options, validFor: WORD } : null;
|
|
839
|
+
};
|
|
784
840
|
var dbt = simpleMode({
|
|
841
|
+
languageData: {
|
|
842
|
+
// `toggleComment` is a no-op without these, so Mod-/ silently does nothing.
|
|
843
|
+
commentTokens: { line: "--", block: { open: "/*", close: "*/" } },
|
|
844
|
+
autocomplete: complete
|
|
845
|
+
},
|
|
785
846
|
start: [
|
|
786
847
|
{ regex: /"(?:[^\\]|\\.)*?(?:"|$)/, token: "string" },
|
|
787
|
-
|
|
788
|
-
|
|
848
|
+
// Word-bounded, so `dbtX` is not a dbt step whose first word happens to
|
|
849
|
+
// colour — the same thing the completion source asks of it.
|
|
850
|
+
{ regex: /dbt\b/, token: "keyword", sol: true },
|
|
851
|
+
{ regex: word(COMMAND_PHRASES), token: "keyword" },
|
|
789
852
|
{ regex: word(ARGUMENTS), token: "variableName" },
|
|
790
853
|
{ regex: prefix(METHODS), token: "propertyName" },
|
|
791
854
|
{ regex: prefix(FLAGS), token: "attributeName" },
|
|
@@ -833,6 +896,8 @@ var KEYWORDS = {
|
|
|
833
896
|
};
|
|
834
897
|
var jsonnet = {
|
|
835
898
|
name: "jsonnet",
|
|
899
|
+
// `toggleComment` is a no-op without these, so Mod-/ silently does nothing.
|
|
900
|
+
languageData: { commentTokens: { line: "//", block: { open: "/*", close: "*/" } } },
|
|
836
901
|
startState: () => ({
|
|
837
902
|
cComment: false,
|
|
838
903
|
textBlock: false,
|
|
@@ -1049,6 +1114,20 @@ var buildCustomTheme = () => githubLightInit({
|
|
|
1049
1114
|
selection: resolveToken("primary", "31 143 255")
|
|
1050
1115
|
}
|
|
1051
1116
|
});
|
|
1117
|
+
var sfsqlWithLanguageData = {
|
|
1118
|
+
...sfsql,
|
|
1119
|
+
languageData: {
|
|
1120
|
+
...sfsql.languageData,
|
|
1121
|
+
// `toggleComment` is a no-op without these, so Mod-/ silently does nothing.
|
|
1122
|
+
commentTokens: { line: "--", block: { open: "/*", close: "*/" } },
|
|
1123
|
+
autocomplete: completeFromList(
|
|
1124
|
+
Object.keys(modeSpec.keywords).map((label) => ({
|
|
1125
|
+
label: label.toUpperCase(),
|
|
1126
|
+
type: "keyword"
|
|
1127
|
+
}))
|
|
1128
|
+
)
|
|
1129
|
+
}
|
|
1130
|
+
};
|
|
1052
1131
|
var getLanguageMode = (mode) => {
|
|
1053
1132
|
switch (mode) {
|
|
1054
1133
|
case void 0:
|
|
@@ -1065,7 +1144,7 @@ var getLanguageMode = (mode) => {
|
|
|
1065
1144
|
case "text/x-mssql":
|
|
1066
1145
|
return sql({ upperCaseKeywords: true, dialect: MSSQL });
|
|
1067
1146
|
case "text/x-sfsql":
|
|
1068
|
-
return StreamLanguage.define(
|
|
1147
|
+
return StreamLanguage.define(sfsqlWithLanguageData);
|
|
1069
1148
|
case "text/x-plsql":
|
|
1070
1149
|
return sql({ upperCaseKeywords: true, dialect: PLSQL });
|
|
1071
1150
|
case "text/x-rsrc":
|
|
@@ -1090,6 +1169,112 @@ var getLanguageMode = (mode) => {
|
|
|
1090
1169
|
}
|
|
1091
1170
|
};
|
|
1092
1171
|
var shouldLintJson = (mode, readOnly) => mode === "application/json" && !readOnly;
|
|
1172
|
+
var setSearchEffect = StateEffect.define();
|
|
1173
|
+
function findMatches(doc, query) {
|
|
1174
|
+
if (!query) return [];
|
|
1175
|
+
const text = doc.toString();
|
|
1176
|
+
try {
|
|
1177
|
+
const regex = new RegExp(escapeStringRegexp(query), "gi");
|
|
1178
|
+
return Array.from(text.matchAll(regex)).map((match, index) => {
|
|
1179
|
+
const from = match.index;
|
|
1180
|
+
const to = from + match[0].length;
|
|
1181
|
+
return { from, to, text: match[0], localIndex: index };
|
|
1182
|
+
});
|
|
1183
|
+
} catch (e) {
|
|
1184
|
+
console.warn("Invalid search regex:", e);
|
|
1185
|
+
return [];
|
|
1186
|
+
}
|
|
1187
|
+
}
|
|
1188
|
+
function createDecorations(matches, selectedLocalIndex) {
|
|
1189
|
+
const decorations = matches.map((match) => {
|
|
1190
|
+
const isSelected = match.localIndex === selectedLocalIndex;
|
|
1191
|
+
const decoration = Decoration.mark({
|
|
1192
|
+
class: isSelected ? "cm-searchMatch-selected" : "cm-searchMatch"
|
|
1193
|
+
});
|
|
1194
|
+
return decoration.range(match.from, match.to);
|
|
1195
|
+
});
|
|
1196
|
+
return Decoration.set(decorations, true);
|
|
1197
|
+
}
|
|
1198
|
+
var searchField = StateField.define({
|
|
1199
|
+
create() {
|
|
1200
|
+
return {
|
|
1201
|
+
query: "",
|
|
1202
|
+
selectedMatchIndex: void 0,
|
|
1203
|
+
matches: [],
|
|
1204
|
+
decorations: Decoration.none
|
|
1205
|
+
};
|
|
1206
|
+
},
|
|
1207
|
+
update(state, tr) {
|
|
1208
|
+
let newState = state;
|
|
1209
|
+
for (const effect of tr.effects) {
|
|
1210
|
+
if (!effect.is(setSearchEffect)) {
|
|
1211
|
+
continue;
|
|
1212
|
+
}
|
|
1213
|
+
const { query, selectedMatchIndex } = effect.value;
|
|
1214
|
+
const matches = findMatches(tr.state.doc, query);
|
|
1215
|
+
const decorations = createDecorations(matches, selectedMatchIndex);
|
|
1216
|
+
newState = {
|
|
1217
|
+
query,
|
|
1218
|
+
selectedMatchIndex,
|
|
1219
|
+
matches,
|
|
1220
|
+
decorations
|
|
1221
|
+
};
|
|
1222
|
+
}
|
|
1223
|
+
if (tr.docChanged && newState.query) {
|
|
1224
|
+
const matches = findMatches(tr.state.doc, newState.query);
|
|
1225
|
+
const decorations = createDecorations(matches, newState.selectedMatchIndex);
|
|
1226
|
+
newState = {
|
|
1227
|
+
...newState,
|
|
1228
|
+
matches,
|
|
1229
|
+
decorations
|
|
1230
|
+
};
|
|
1231
|
+
}
|
|
1232
|
+
return newState;
|
|
1233
|
+
},
|
|
1234
|
+
provide: (field) => EditorView.decorations.from(field, (state) => state.decorations)
|
|
1235
|
+
});
|
|
1236
|
+
var searchViewPlugin = ViewPlugin.fromClass(
|
|
1237
|
+
class {
|
|
1238
|
+
lastSelectedIndex;
|
|
1239
|
+
update(update) {
|
|
1240
|
+
const state = update.state.field(searchField);
|
|
1241
|
+
if (state.selectedMatchIndex !== void 0 && state.selectedMatchIndex !== this.lastSelectedIndex) {
|
|
1242
|
+
const match = state.matches[state.selectedMatchIndex];
|
|
1243
|
+
if (match) {
|
|
1244
|
+
requestAnimationFrame(() => {
|
|
1245
|
+
update.view.dispatch({
|
|
1246
|
+
effects: EditorView.scrollIntoView(match.from, { y: "center" })
|
|
1247
|
+
});
|
|
1248
|
+
});
|
|
1249
|
+
}
|
|
1250
|
+
this.lastSelectedIndex = state.selectedMatchIndex;
|
|
1251
|
+
}
|
|
1252
|
+
if (state.selectedMatchIndex === void 0) {
|
|
1253
|
+
this.lastSelectedIndex = void 0;
|
|
1254
|
+
}
|
|
1255
|
+
}
|
|
1256
|
+
}
|
|
1257
|
+
);
|
|
1258
|
+
var searchTheme = EditorView.baseTheme({
|
|
1259
|
+
".cm-searchMatch": {
|
|
1260
|
+
backgroundColor: colors.blue[100]
|
|
1261
|
+
},
|
|
1262
|
+
".cm-searchMatch > span": {
|
|
1263
|
+
color: `${colors.blue[600]} !important`
|
|
1264
|
+
},
|
|
1265
|
+
".cm-searchMatch-selected": {
|
|
1266
|
+
backgroundColor: colors.blue[600]
|
|
1267
|
+
},
|
|
1268
|
+
".cm-searchMatch-selected > span": {
|
|
1269
|
+
color: `${colors.white} !important`
|
|
1270
|
+
}
|
|
1271
|
+
});
|
|
1272
|
+
function searchExtension() {
|
|
1273
|
+
return [searchField, searchViewPlugin, searchTheme];
|
|
1274
|
+
}
|
|
1275
|
+
function updateSearch(view, query, selectedMatchIndex) {
|
|
1276
|
+
view.dispatch({ effects: setSearchEffect.of({ query, selectedMatchIndex }) });
|
|
1277
|
+
}
|
|
1093
1278
|
var KbdSeparator = ({ className, ...props }) => /* @__PURE__ */ jsx("span", { ...props, className: cn("tw:text-xs tw:text-muted-foreground", className) });
|
|
1094
1279
|
var KbdGroup = ({ ref, className, children, separator, ...props }) => {
|
|
1095
1280
|
return /* @__PURE__ */ jsx(
|
|
@@ -1149,11 +1334,78 @@ Kbd.Group = KbdGroup;
|
|
|
1149
1334
|
Kbd.Separator = KbdSeparator;
|
|
1150
1335
|
var ToggleComponentHelp = () => /* @__PURE__ */ jsxs("div", { children: [
|
|
1151
1336
|
"Use ",
|
|
1152
|
-
/* @__PURE__ */ jsx(Kbd, { children: "Ctrl+/" }),
|
|
1153
|
-
" or ",
|
|
1154
|
-
/* @__PURE__ */ jsx(Kbd, { children: "Command+/" }),
|
|
1337
|
+
/* @__PURE__ */ jsx(Kbd, { children: isMac ? "Command+/" : "Ctrl+/" }),
|
|
1155
1338
|
" to toggle comments."
|
|
1156
1339
|
] });
|
|
1340
|
+
var establishesFixedContainingBlock = (style) => style.transform !== "none" || style.translate !== "none" || style.rotate !== "none" || style.scale !== "none" || style.perspective !== "none" || style.filter !== "none" || style.backdropFilter !== "none" || /\b(paint|layout|strict|content)\b/.test(style.contain) || /\b(transform|perspective|filter|contain)\b/.test(style.willChange);
|
|
1341
|
+
var crops = (style) => style.overflowX !== "visible" || style.overflowY !== "visible" || style.maskImage !== "none" || style.contentVisibility !== "visible" || /\b(paint|strict|content)\b/.test(style.contain);
|
|
1342
|
+
var findTooltipHost = (from) => {
|
|
1343
|
+
let outermostCropper = null;
|
|
1344
|
+
for (let node = from.parentElement; node && node !== document.body; node = node.parentElement) {
|
|
1345
|
+
const style = getComputedStyle(node);
|
|
1346
|
+
if (crops(style)) {
|
|
1347
|
+
outermostCropper = node;
|
|
1348
|
+
} else if (establishesFixedContainingBlock(style)) {
|
|
1349
|
+
return outermostCropper?.parentElement ?? node;
|
|
1350
|
+
}
|
|
1351
|
+
}
|
|
1352
|
+
return outermostCropper?.parentElement ?? null;
|
|
1353
|
+
};
|
|
1354
|
+
var tooltipsExtension = () => {
|
|
1355
|
+
const layer = new Compartment();
|
|
1356
|
+
return [
|
|
1357
|
+
layer.of(tooltips()),
|
|
1358
|
+
ViewPlugin.define((view) => {
|
|
1359
|
+
const scheduled = setTimeout(() => {
|
|
1360
|
+
if (!view.dom.isConnected) return;
|
|
1361
|
+
const parent = findTooltipHost(view.dom);
|
|
1362
|
+
if (parent) view.dispatch({ effects: layer.reconfigure(tooltips({ parent })) });
|
|
1363
|
+
});
|
|
1364
|
+
return { destroy: () => clearTimeout(scheduled) };
|
|
1365
|
+
})
|
|
1366
|
+
];
|
|
1367
|
+
};
|
|
1368
|
+
var useTooltipsExtension = () => useMemo(tooltipsExtension, []);
|
|
1369
|
+
|
|
1370
|
+
// src/components/CodeEditor/variableCompletion.ts
|
|
1371
|
+
var BOOST = 2e3;
|
|
1372
|
+
var NAME = String.raw`[\w-]*`;
|
|
1373
|
+
var AT_CARET = new RegExp(String.raw`(?:\{{1,2} ?)?${NAME}$`);
|
|
1374
|
+
var NAME_AT_END = new RegExp(`${NAME}$`);
|
|
1375
|
+
var OPENING = /\{{1,2} ?$/;
|
|
1376
|
+
var CLOSING = /^ ?\}{1,2}/;
|
|
1377
|
+
var completeVariables = (variables = []) => {
|
|
1378
|
+
if (variables.length === 0) return () => null;
|
|
1379
|
+
const options = variables.map(({ name, value }) => ({
|
|
1380
|
+
// The bare name: `SqlEditor` filters prefix-only while `CodeEditor` filters
|
|
1381
|
+
// fuzzily, and this is the only spelling both accept whether or not the caret
|
|
1382
|
+
// has typed the braces. The braces are `displayLabel`, which is what renders.
|
|
1383
|
+
label: name,
|
|
1384
|
+
displayLabel: `{{${name}}}`,
|
|
1385
|
+
detail: value,
|
|
1386
|
+
type: "variable",
|
|
1387
|
+
boost: BOOST,
|
|
1388
|
+
apply: (view, completion, from, to) => {
|
|
1389
|
+
const { doc } = view.state;
|
|
1390
|
+
const line = doc.lineAt(from);
|
|
1391
|
+
const insert = `{{${completion.label}}}`;
|
|
1392
|
+
const opening = OPENING.exec(doc.sliceString(line.from, from));
|
|
1393
|
+
const closing = CLOSING.exec(doc.sliceString(to, line.to));
|
|
1394
|
+
const start = opening ? from - opening[0].length : from;
|
|
1395
|
+
const end = closing ? to + closing[0].length : to;
|
|
1396
|
+
view.dispatch({
|
|
1397
|
+
changes: { from: start, to: end, insert },
|
|
1398
|
+
selection: { anchor: start + insert.length }
|
|
1399
|
+
});
|
|
1400
|
+
}
|
|
1401
|
+
}));
|
|
1402
|
+
return (context) => {
|
|
1403
|
+
const match = context.matchBefore(AT_CARET);
|
|
1404
|
+
if (!match?.text) return null;
|
|
1405
|
+
const [name = ""] = NAME_AT_END.exec(match.text) ?? [];
|
|
1406
|
+
return { from: match.to - name.length, options };
|
|
1407
|
+
};
|
|
1408
|
+
};
|
|
1157
1409
|
var emptyArray = [];
|
|
1158
1410
|
var emptyObject = {};
|
|
1159
1411
|
var CodeEditor = ({
|
|
@@ -1163,8 +1415,12 @@ var CodeEditor = ({
|
|
|
1163
1415
|
mode = "application/json",
|
|
1164
1416
|
placeholder: placeholder$1 = "",
|
|
1165
1417
|
ariaLabel,
|
|
1418
|
+
searchQuery,
|
|
1419
|
+
searchMatchIndex,
|
|
1166
1420
|
readOnly = false,
|
|
1167
1421
|
autocompletions = emptyArray,
|
|
1422
|
+
completionSource,
|
|
1423
|
+
variables,
|
|
1168
1424
|
extraKeys = emptyObject,
|
|
1169
1425
|
withToggleCommentHint = false,
|
|
1170
1426
|
withAutofocus = false,
|
|
@@ -1179,6 +1435,26 @@ var CodeEditor = ({
|
|
|
1179
1435
|
className
|
|
1180
1436
|
}) => {
|
|
1181
1437
|
const handleChange = useStableCallback((next) => onChange?.(next));
|
|
1438
|
+
const withSearch = searchQuery !== void 0;
|
|
1439
|
+
const editorRef = useRef(null);
|
|
1440
|
+
useEffect(() => {
|
|
1441
|
+
const view = editorRef.current?.view;
|
|
1442
|
+
if (view && withSearch) updateSearch(view, searchQuery, searchMatchIndex);
|
|
1443
|
+
}, [withSearch, searchQuery, searchMatchIndex]);
|
|
1444
|
+
const handleCreateEditor = useStableCallback((view) => {
|
|
1445
|
+
if (searchQuery) updateSearch(view, searchQuery, searchMatchIndex);
|
|
1446
|
+
});
|
|
1447
|
+
const extraKeysRef = useRef(extraKeys);
|
|
1448
|
+
useLayoutEffect(() => {
|
|
1449
|
+
extraKeysRef.current = extraKeys;
|
|
1450
|
+
});
|
|
1451
|
+
const boundKeysSignature = Object.keys(extraKeys).join("\n");
|
|
1452
|
+
const boundKeys = useMemo(
|
|
1453
|
+
() => boundKeysSignature.split("\n").filter(Boolean),
|
|
1454
|
+
[boundKeysSignature]
|
|
1455
|
+
);
|
|
1456
|
+
const tooltipLayer = useTooltipsExtension();
|
|
1457
|
+
const variableCompletion = useMemo(() => completeVariables(variables), [variables]);
|
|
1182
1458
|
const customTheme = useThemedExtension("editor:syntax", buildCustomTheme);
|
|
1183
1459
|
const baseTheme = useThemedExtension(
|
|
1184
1460
|
"code-editor:chrome",
|
|
@@ -1190,31 +1466,43 @@ var CodeEditor = ({
|
|
|
1190
1466
|
shouldLintJson(mode, readOnly) ? linter(jsonParseLinter()) : [],
|
|
1191
1467
|
withLineWrapping ? EditorView.lineWrapping : [],
|
|
1192
1468
|
EditorState.languageData.of(() => [
|
|
1193
|
-
{ autocomplete: completeAny ? completeAnyWord : autocompletions }
|
|
1469
|
+
{ autocomplete: completionSource ?? (completeAny ? completeAnyWord : autocompletions) }
|
|
1194
1470
|
]),
|
|
1471
|
+
EditorState.languageData.of(() => [{ autocomplete: variableCompletion }]),
|
|
1195
1472
|
keymap.of(
|
|
1196
|
-
|
|
1473
|
+
boundKeys.map((key) => ({
|
|
1197
1474
|
key,
|
|
1198
1475
|
run: () => {
|
|
1199
|
-
|
|
1200
|
-
run();
|
|
1476
|
+
extraKeysRef.current[key]?.();
|
|
1201
1477
|
return true;
|
|
1202
1478
|
}
|
|
1203
1479
|
}))
|
|
1204
1480
|
),
|
|
1205
|
-
readOnly ? [] :
|
|
1481
|
+
readOnly ? [] : [
|
|
1482
|
+
// CodeMirror's own default. Gating it on the props hid completions the
|
|
1483
|
+
// *language* supplies — dbt's vocabulary, Snowflake's keywords — behind
|
|
1484
|
+
// a shortcut, and macOS leaves no usable one. With nothing to offer, no
|
|
1485
|
+
// popup opens anyway.
|
|
1486
|
+
autocompletion()
|
|
1487
|
+
],
|
|
1206
1488
|
ariaLabel ? EditorView.contentAttributes.of({ "aria-label": ariaLabel }) : [],
|
|
1489
|
+
withSearch ? searchExtension() : [],
|
|
1490
|
+
tooltipLayer,
|
|
1207
1491
|
baseTheme
|
|
1208
1492
|
];
|
|
1209
1493
|
}, [
|
|
1210
1494
|
autocompletions,
|
|
1495
|
+
completionSource,
|
|
1496
|
+
variableCompletion,
|
|
1211
1497
|
completeAny,
|
|
1212
|
-
|
|
1498
|
+
boundKeys,
|
|
1213
1499
|
mode,
|
|
1214
1500
|
readOnly,
|
|
1215
1501
|
withLineWrapping,
|
|
1216
1502
|
baseTheme,
|
|
1217
|
-
ariaLabel
|
|
1503
|
+
ariaLabel,
|
|
1504
|
+
withSearch,
|
|
1505
|
+
tooltipLayer
|
|
1218
1506
|
]);
|
|
1219
1507
|
const extensions = useMemo(() => {
|
|
1220
1508
|
return [
|
|
@@ -1233,9 +1521,12 @@ var CodeEditor = ({
|
|
|
1233
1521
|
const config = useMemo(() => {
|
|
1234
1522
|
return {
|
|
1235
1523
|
...buildBasicSetup(readOnly),
|
|
1236
|
-
lineNumbers: withLineNumbers
|
|
1524
|
+
lineNumbers: withLineNumbers,
|
|
1525
|
+
// Mod-f opens CodeMirror's own panel, which would run a second query and a
|
|
1526
|
+
// second match count over the same document beside the caller's input.
|
|
1527
|
+
...withSearch ? { searchKeymap: false } : null
|
|
1237
1528
|
};
|
|
1238
|
-
}, [withLineNumbers, readOnly]);
|
|
1529
|
+
}, [withLineNumbers, readOnly, withSearch]);
|
|
1239
1530
|
const additionalButtons = renderAdditionalButtons?.(value);
|
|
1240
1531
|
const hasToolbar = withCopyButton && !!value || !!additionalButtons;
|
|
1241
1532
|
return (
|
|
@@ -1258,6 +1549,8 @@ var CodeEditor = ({
|
|
|
1258
1549
|
/* @__PURE__ */ jsx(
|
|
1259
1550
|
CodeMirror,
|
|
1260
1551
|
{
|
|
1552
|
+
ref: editorRef,
|
|
1553
|
+
onCreateEditor: handleCreateEditor,
|
|
1261
1554
|
theme: customTheme,
|
|
1262
1555
|
value,
|
|
1263
1556
|
onChange: handleChange,
|
|
@@ -1580,112 +1873,6 @@ function highlightingExtension(queryParser) {
|
|
|
1580
1873
|
function updateHighlighting(view, lines) {
|
|
1581
1874
|
view.dispatch({ effects: setHighlightedLinesEffect.of(lines ?? null) });
|
|
1582
1875
|
}
|
|
1583
|
-
var setSearchEffect = StateEffect.define();
|
|
1584
|
-
function findMatches(doc, query) {
|
|
1585
|
-
if (!query) return [];
|
|
1586
|
-
const text = doc.toString();
|
|
1587
|
-
try {
|
|
1588
|
-
const regex = new RegExp(escapeStringRegexp(query), "gi");
|
|
1589
|
-
return Array.from(text.matchAll(regex)).map((match, index) => {
|
|
1590
|
-
const from = match.index;
|
|
1591
|
-
const to = from + match[0].length;
|
|
1592
|
-
return { from, to, text: match[0], localIndex: index };
|
|
1593
|
-
});
|
|
1594
|
-
} catch (e) {
|
|
1595
|
-
console.warn("Invalid search regex:", e);
|
|
1596
|
-
return [];
|
|
1597
|
-
}
|
|
1598
|
-
}
|
|
1599
|
-
function createDecorations(matches, selectedLocalIndex) {
|
|
1600
|
-
const decorations = matches.map((match) => {
|
|
1601
|
-
const isSelected = match.localIndex === selectedLocalIndex;
|
|
1602
|
-
const decoration = Decoration.mark({
|
|
1603
|
-
class: isSelected ? "cm-searchMatch-selected" : "cm-searchMatch"
|
|
1604
|
-
});
|
|
1605
|
-
return decoration.range(match.from, match.to);
|
|
1606
|
-
});
|
|
1607
|
-
return Decoration.set(decorations, true);
|
|
1608
|
-
}
|
|
1609
|
-
var searchField = StateField.define({
|
|
1610
|
-
create() {
|
|
1611
|
-
return {
|
|
1612
|
-
query: "",
|
|
1613
|
-
selectedMatchIndex: void 0,
|
|
1614
|
-
matches: [],
|
|
1615
|
-
decorations: Decoration.none
|
|
1616
|
-
};
|
|
1617
|
-
},
|
|
1618
|
-
update(state, tr) {
|
|
1619
|
-
let newState = state;
|
|
1620
|
-
for (const effect of tr.effects) {
|
|
1621
|
-
if (!effect.is(setSearchEffect)) {
|
|
1622
|
-
continue;
|
|
1623
|
-
}
|
|
1624
|
-
const { query, selectedMatchIndex } = effect.value;
|
|
1625
|
-
const matches = findMatches(tr.state.doc, query);
|
|
1626
|
-
const decorations = createDecorations(matches, selectedMatchIndex);
|
|
1627
|
-
newState = {
|
|
1628
|
-
query,
|
|
1629
|
-
selectedMatchIndex,
|
|
1630
|
-
matches,
|
|
1631
|
-
decorations
|
|
1632
|
-
};
|
|
1633
|
-
}
|
|
1634
|
-
if (tr.docChanged && newState.query) {
|
|
1635
|
-
const matches = findMatches(tr.state.doc, newState.query);
|
|
1636
|
-
const decorations = createDecorations(matches, newState.selectedMatchIndex);
|
|
1637
|
-
newState = {
|
|
1638
|
-
...newState,
|
|
1639
|
-
matches,
|
|
1640
|
-
decorations
|
|
1641
|
-
};
|
|
1642
|
-
}
|
|
1643
|
-
return newState;
|
|
1644
|
-
},
|
|
1645
|
-
provide: (field) => EditorView.decorations.from(field, (state) => state.decorations)
|
|
1646
|
-
});
|
|
1647
|
-
var searchViewPlugin = ViewPlugin.fromClass(
|
|
1648
|
-
class {
|
|
1649
|
-
lastSelectedIndex;
|
|
1650
|
-
update(update) {
|
|
1651
|
-
const state = update.state.field(searchField);
|
|
1652
|
-
if (state.selectedMatchIndex !== void 0 && state.selectedMatchIndex !== this.lastSelectedIndex) {
|
|
1653
|
-
const match = state.matches[state.selectedMatchIndex];
|
|
1654
|
-
if (match) {
|
|
1655
|
-
requestAnimationFrame(() => {
|
|
1656
|
-
update.view.dispatch({
|
|
1657
|
-
effects: EditorView.scrollIntoView(match.from, { y: "center" })
|
|
1658
|
-
});
|
|
1659
|
-
});
|
|
1660
|
-
}
|
|
1661
|
-
this.lastSelectedIndex = state.selectedMatchIndex;
|
|
1662
|
-
}
|
|
1663
|
-
if (state.selectedMatchIndex === void 0) {
|
|
1664
|
-
this.lastSelectedIndex = void 0;
|
|
1665
|
-
}
|
|
1666
|
-
}
|
|
1667
|
-
}
|
|
1668
|
-
);
|
|
1669
|
-
var searchTheme = EditorView.baseTheme({
|
|
1670
|
-
".cm-searchMatch": {
|
|
1671
|
-
backgroundColor: colors.blue[100]
|
|
1672
|
-
},
|
|
1673
|
-
".cm-searchMatch > span": {
|
|
1674
|
-
color: `${colors.blue[600]} !important`
|
|
1675
|
-
},
|
|
1676
|
-
".cm-searchMatch-selected": {
|
|
1677
|
-
backgroundColor: colors.blue[600]
|
|
1678
|
-
},
|
|
1679
|
-
".cm-searchMatch-selected > span": {
|
|
1680
|
-
color: `${colors.white} !important`
|
|
1681
|
-
}
|
|
1682
|
-
});
|
|
1683
|
-
function searchExtension() {
|
|
1684
|
-
return [searchField, searchViewPlugin, searchTheme];
|
|
1685
|
-
}
|
|
1686
|
-
function updateSearch(view, query, selectedMatchIndex) {
|
|
1687
|
-
view.dispatch({ effects: setSearchEffect.of({ query, selectedMatchIndex }) });
|
|
1688
|
-
}
|
|
1689
1876
|
var OWN_KEYMAPS = {
|
|
1690
1877
|
defaultKeymap: false,
|
|
1691
1878
|
completionKeymap: false,
|
|
@@ -1751,6 +1938,7 @@ var SqlEditor = ({
|
|
|
1751
1938
|
dialect
|
|
1752
1939
|
}) => {
|
|
1753
1940
|
const editorRef = useRef(null);
|
|
1941
|
+
const tooltipLayer = useTooltipsExtension();
|
|
1754
1942
|
const customTheme = useThemedExtension("editor:syntax", buildCustomTheme);
|
|
1755
1943
|
const themeExtension = useThemedExtension(
|
|
1756
1944
|
"sql-editor:chrome",
|
|
@@ -1761,9 +1949,16 @@ var SqlEditor = ({
|
|
|
1761
1949
|
return [
|
|
1762
1950
|
EditorView$1.lineWrapping,
|
|
1763
1951
|
keymap$1.of(extraKeys ? [...extraKeys, ...baseKeymap] : baseKeymap),
|
|
1764
|
-
...installExtensions({
|
|
1952
|
+
...installExtensions({
|
|
1953
|
+
schema,
|
|
1954
|
+
defaultDatabase,
|
|
1955
|
+
defaultSchema,
|
|
1956
|
+
variableCompletion: completeVariables(variables),
|
|
1957
|
+
dialect
|
|
1958
|
+
}),
|
|
1765
1959
|
searchExtension(),
|
|
1766
1960
|
highlightingExtension(queryParser),
|
|
1961
|
+
tooltipLayer,
|
|
1767
1962
|
themeExtension,
|
|
1768
1963
|
onBlurEditor ? EditorView$1.domEventHandlers({ blur: (_, view) => onBlurEditor(view) }) : [],
|
|
1769
1964
|
onFocusEditor ? EditorView$1.domEventHandlers({ focus: (_, view) => onFocusEditor(view) }) : []
|
|
@@ -1778,7 +1973,8 @@ var SqlEditor = ({
|
|
|
1778
1973
|
dialect,
|
|
1779
1974
|
onBlurEditor,
|
|
1780
1975
|
onFocusEditor,
|
|
1781
|
-
themeExtension
|
|
1976
|
+
themeExtension,
|
|
1977
|
+
tooltipLayer
|
|
1782
1978
|
]);
|
|
1783
1979
|
const defaultSetup = useMemo(() => {
|
|
1784
1980
|
return { ...buildBasicSetup(readOnly), ...OWN_KEYMAPS, lineNumbers };
|