@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/rjsf.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import React26, { createContext, use, useCallback, useMemo, useRef, useState, useEffect, useContext, isValidElement,
|
|
1
|
+
import React26, { createContext, use, useCallback, useMemo, useRef, useState, useEffect, useContext, isValidElement, useLayoutEffect, useId, useSyncExternalStore, Children, Fragment as Fragment$1 } from 'react';
|
|
2
2
|
import Form2 from '@rjsf/core';
|
|
3
3
|
import validator from '@rjsf/validator-ajv8';
|
|
4
|
-
import { completeAnyWord, autocompletion } from '@codemirror/autocomplete';
|
|
4
|
+
import { completeFromList, completeAnyWord, autocompletion } from '@codemirror/autocomplete';
|
|
5
5
|
import { jsonParseLinter, json } from '@codemirror/lang-json';
|
|
6
6
|
import { linter } from '@codemirror/lint';
|
|
7
7
|
import { unifiedMergeView } from '@codemirror/merge';
|
|
8
|
-
import { EditorView, keymap, placeholder } from '@codemirror/view';
|
|
8
|
+
import { EditorView, Decoration, ViewPlugin, keymap, placeholder, tooltips } from '@codemirror/view';
|
|
9
9
|
import CodeMirror, { EditorState } from '@uiw/react-codemirror';
|
|
10
10
|
import { clsx } from 'clsx';
|
|
11
11
|
import { extendTailwindMerge } from 'tailwind-merge';
|
|
@@ -23,9 +23,10 @@ import { r } from '@codemirror/legacy-modes/mode/r';
|
|
|
23
23
|
import { shell } from '@codemirror/legacy-modes/mode/shell';
|
|
24
24
|
import { toml } from '@codemirror/legacy-modes/mode/toml';
|
|
25
25
|
import { githubLightInit } from '@uiw/codemirror-theme-github';
|
|
26
|
-
import { sfsql } from '@keboola/codemirror-lang-sfsql';
|
|
26
|
+
import { modeSpec, sfsql } from '@keboola/codemirror-lang-sfsql';
|
|
27
27
|
import colors from '@keboola/tailwind-config/colors';
|
|
28
28
|
import { simpleMode } from '@codemirror/legacy-modes/mode/simple-mode';
|
|
29
|
+
import { StateEffect, StateField, Compartment } from '@codemirror/state';
|
|
29
30
|
import DOMPurify from 'dompurify';
|
|
30
31
|
import { Root as Root$2, CollapsibleTrigger, Content as Content$1 } from '@radix-ui/react-collapsible';
|
|
31
32
|
import * as RadixTabs from '@radix-ui/react-tabs';
|
|
@@ -352,6 +353,9 @@ Tooltip.Trigger = TooltipTrigger;
|
|
|
352
353
|
// src/utils/createPrefixedString.ts
|
|
353
354
|
var createPrefixedString = (prefix2) => (literals) => literals.reduce((acc, literal) => acc + literal, `${prefix2}-`);
|
|
354
355
|
|
|
356
|
+
// src/utils/escapeStringRegexp.ts
|
|
357
|
+
var escapeStringRegexp = (string) => string.replace(/[|\\{}()[\]^$+*?.]/g, "\\$&").replace(/-/g, "\\x2d");
|
|
358
|
+
|
|
355
359
|
// src/utils/mergeRefs.ts
|
|
356
360
|
function setRef(ref, value) {
|
|
357
361
|
if (typeof ref === "function") {
|
|
@@ -363,6 +367,21 @@ function setRef(ref, value) {
|
|
|
363
367
|
var mergeRefs = (...refs) => {
|
|
364
368
|
return (node) => refs.forEach((ref) => setRef(ref, node));
|
|
365
369
|
};
|
|
370
|
+
|
|
371
|
+
// src/utils/isMac.ts
|
|
372
|
+
var getIsMac = () => {
|
|
373
|
+
if (typeof navigator !== "undefined" && "userAgentData" in navigator) {
|
|
374
|
+
const platform = navigator.userAgentData.platform;
|
|
375
|
+
if (typeof platform === "string") {
|
|
376
|
+
return platform.toLowerCase() === "macos";
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
if (typeof navigator !== "undefined" && typeof navigator.platform === "string") {
|
|
380
|
+
return navigator.platform.toUpperCase().indexOf("MAC") >= 0;
|
|
381
|
+
}
|
|
382
|
+
return false;
|
|
383
|
+
};
|
|
384
|
+
var isMac = getIsMac();
|
|
366
385
|
var helpVariants = cva(["tw:m-0", "tw:text-xs"], {
|
|
367
386
|
defaultVariants: {
|
|
368
387
|
state: "default"
|
|
@@ -813,8 +832,6 @@ var ButtonToolbar = ({ value, children }) => {
|
|
|
813
832
|
};
|
|
814
833
|
var COMMANDS = [
|
|
815
834
|
"run-operation",
|
|
816
|
-
"source freshness",
|
|
817
|
-
"docs generate",
|
|
818
835
|
"run",
|
|
819
836
|
"test",
|
|
820
837
|
"seed",
|
|
@@ -823,9 +840,13 @@ var COMMANDS = [
|
|
|
823
840
|
"ls",
|
|
824
841
|
"compile",
|
|
825
842
|
"build",
|
|
826
|
-
"deps"
|
|
843
|
+
"deps",
|
|
844
|
+
"docs",
|
|
845
|
+
"source"
|
|
827
846
|
];
|
|
847
|
+
var SUBCOMMANDS = { docs: ["generate"], source: ["freshness"] };
|
|
828
848
|
var ARGUMENTS = ["--select", "--exclude", "--selector", "--defer", "--resource-type", "-s"];
|
|
849
|
+
var SELECTORS = ["--select", "--exclude", "-s"];
|
|
829
850
|
var METHODS = [
|
|
830
851
|
"path:",
|
|
831
852
|
"source:",
|
|
@@ -852,13 +873,59 @@ var FLAGS = [
|
|
|
852
873
|
"--profile",
|
|
853
874
|
"--full-refresh"
|
|
854
875
|
];
|
|
855
|
-
var
|
|
856
|
-
|
|
876
|
+
var COMMAND_PHRASES = COMMANDS.flatMap((command) => {
|
|
877
|
+
const subcommands = SUBCOMMANDS[command];
|
|
878
|
+
return subcommands ? subcommands.map((subcommand) => `${command} ${subcommand}`) : [command];
|
|
879
|
+
});
|
|
880
|
+
var alternation = (list) => [...list].sort((a, b) => b.length - a.length).join("|");
|
|
881
|
+
var word = (list) => new RegExp(`^\\s+(?:${alternation(list)})\\b`, "i");
|
|
882
|
+
var prefix = (list) => new RegExp(`^\\s+(?:${alternation(list)})`, "i");
|
|
883
|
+
var WORD = /[\w:-]*$/;
|
|
884
|
+
var asOptions = (list, type) => list.map((label) => ({ label, type }));
|
|
885
|
+
var DBT = [{ label: "dbt", type: "keyword" }];
|
|
886
|
+
var COMMAND_OPTIONS = asOptions(COMMANDS, "keyword");
|
|
887
|
+
var SUBCOMMAND_OPTIONS = Object.fromEntries(
|
|
888
|
+
Object.entries(SUBCOMMANDS).map(([command, subcommands]) => [
|
|
889
|
+
command,
|
|
890
|
+
asOptions(subcommands, "keyword")
|
|
891
|
+
])
|
|
892
|
+
);
|
|
893
|
+
var ARGUMENT_OPTIONS = asOptions(ARGUMENTS, "variable");
|
|
894
|
+
var FLAG_OPTIONS = asOptions(FLAGS, "constant");
|
|
895
|
+
var METHOD_OPTIONS = asOptions(METHODS, "property");
|
|
896
|
+
var optionsAfter = (words) => {
|
|
897
|
+
const [step, command, ...rest] = words;
|
|
898
|
+
if (step === void 0) return DBT;
|
|
899
|
+
if (step.toLowerCase() !== "dbt") return [];
|
|
900
|
+
if (command === void 0) return COMMAND_OPTIONS;
|
|
901
|
+
const subcommands = SUBCOMMAND_OPTIONS[command.toLowerCase()];
|
|
902
|
+
if (subcommands && rest.length === 0) return subcommands;
|
|
903
|
+
const lastArgument = rest.filter((it) => it.startsWith("-")).at(-1);
|
|
904
|
+
const selecting = lastArgument !== void 0 && SELECTORS.includes(lastArgument.toLowerCase());
|
|
905
|
+
return selecting ? [...ARGUMENT_OPTIONS, ...FLAG_OPTIONS, ...METHOD_OPTIONS] : [...ARGUMENT_OPTIONS, ...FLAG_OPTIONS];
|
|
906
|
+
};
|
|
907
|
+
var complete = (context) => {
|
|
908
|
+
const typed = context.matchBefore(WORD);
|
|
909
|
+
if (!typed?.text && !context.explicit) return null;
|
|
910
|
+
const from = typed?.from ?? context.pos;
|
|
911
|
+
const line = context.state.doc.lineAt(from);
|
|
912
|
+
const options = optionsAfter(
|
|
913
|
+
line.text.slice(0, from - line.from).split(/\s+/).filter(Boolean)
|
|
914
|
+
);
|
|
915
|
+
return options.length > 0 ? { from, options, validFor: WORD } : null;
|
|
916
|
+
};
|
|
857
917
|
var dbt = simpleMode({
|
|
918
|
+
languageData: {
|
|
919
|
+
// `toggleComment` is a no-op without these, so Mod-/ silently does nothing.
|
|
920
|
+
commentTokens: { line: "--", block: { open: "/*", close: "*/" } },
|
|
921
|
+
autocomplete: complete
|
|
922
|
+
},
|
|
858
923
|
start: [
|
|
859
924
|
{ regex: /"(?:[^\\]|\\.)*?(?:"|$)/, token: "string" },
|
|
860
|
-
|
|
861
|
-
|
|
925
|
+
// Word-bounded, so `dbtX` is not a dbt step whose first word happens to
|
|
926
|
+
// colour — the same thing the completion source asks of it.
|
|
927
|
+
{ regex: /dbt\b/, token: "keyword", sol: true },
|
|
928
|
+
{ regex: word(COMMAND_PHRASES), token: "keyword" },
|
|
862
929
|
{ regex: word(ARGUMENTS), token: "variableName" },
|
|
863
930
|
{ regex: prefix(METHODS), token: "propertyName" },
|
|
864
931
|
{ regex: prefix(FLAGS), token: "attributeName" },
|
|
@@ -906,6 +973,8 @@ var KEYWORDS = {
|
|
|
906
973
|
};
|
|
907
974
|
var jsonnet = {
|
|
908
975
|
name: "jsonnet",
|
|
976
|
+
// `toggleComment` is a no-op without these, so Mod-/ silently does nothing.
|
|
977
|
+
languageData: { commentTokens: { line: "//", block: { open: "/*", close: "*/" } } },
|
|
909
978
|
startState: () => ({
|
|
910
979
|
cComment: false,
|
|
911
980
|
textBlock: false,
|
|
@@ -1122,6 +1191,20 @@ var buildCustomTheme = () => githubLightInit({
|
|
|
1122
1191
|
selection: resolveToken("primary", "31 143 255")
|
|
1123
1192
|
}
|
|
1124
1193
|
});
|
|
1194
|
+
var sfsqlWithLanguageData = {
|
|
1195
|
+
...sfsql,
|
|
1196
|
+
languageData: {
|
|
1197
|
+
...sfsql.languageData,
|
|
1198
|
+
// `toggleComment` is a no-op without these, so Mod-/ silently does nothing.
|
|
1199
|
+
commentTokens: { line: "--", block: { open: "/*", close: "*/" } },
|
|
1200
|
+
autocomplete: completeFromList(
|
|
1201
|
+
Object.keys(modeSpec.keywords).map((label) => ({
|
|
1202
|
+
label: label.toUpperCase(),
|
|
1203
|
+
type: "keyword"
|
|
1204
|
+
}))
|
|
1205
|
+
)
|
|
1206
|
+
}
|
|
1207
|
+
};
|
|
1125
1208
|
var getLanguageMode = (mode) => {
|
|
1126
1209
|
switch (mode) {
|
|
1127
1210
|
case void 0:
|
|
@@ -1138,7 +1221,7 @@ var getLanguageMode = (mode) => {
|
|
|
1138
1221
|
case "text/x-mssql":
|
|
1139
1222
|
return sql({ upperCaseKeywords: true, dialect: MSSQL });
|
|
1140
1223
|
case "text/x-sfsql":
|
|
1141
|
-
return StreamLanguage.define(
|
|
1224
|
+
return StreamLanguage.define(sfsqlWithLanguageData);
|
|
1142
1225
|
case "text/x-plsql":
|
|
1143
1226
|
return sql({ upperCaseKeywords: true, dialect: PLSQL });
|
|
1144
1227
|
case "text/x-rsrc":
|
|
@@ -1163,6 +1246,112 @@ var getLanguageMode = (mode) => {
|
|
|
1163
1246
|
}
|
|
1164
1247
|
};
|
|
1165
1248
|
var shouldLintJson = (mode, readOnly) => mode === "application/json" && !readOnly;
|
|
1249
|
+
var setSearchEffect = StateEffect.define();
|
|
1250
|
+
function findMatches(doc, query) {
|
|
1251
|
+
if (!query) return [];
|
|
1252
|
+
const text = doc.toString();
|
|
1253
|
+
try {
|
|
1254
|
+
const regex = new RegExp(escapeStringRegexp(query), "gi");
|
|
1255
|
+
return Array.from(text.matchAll(regex)).map((match2, index) => {
|
|
1256
|
+
const from = match2.index;
|
|
1257
|
+
const to = from + match2[0].length;
|
|
1258
|
+
return { from, to, text: match2[0], localIndex: index };
|
|
1259
|
+
});
|
|
1260
|
+
} catch (e) {
|
|
1261
|
+
console.warn("Invalid search regex:", e);
|
|
1262
|
+
return [];
|
|
1263
|
+
}
|
|
1264
|
+
}
|
|
1265
|
+
function createDecorations(matches, selectedLocalIndex) {
|
|
1266
|
+
const decorations = matches.map((match2) => {
|
|
1267
|
+
const isSelected = match2.localIndex === selectedLocalIndex;
|
|
1268
|
+
const decoration = Decoration.mark({
|
|
1269
|
+
class: isSelected ? "cm-searchMatch-selected" : "cm-searchMatch"
|
|
1270
|
+
});
|
|
1271
|
+
return decoration.range(match2.from, match2.to);
|
|
1272
|
+
});
|
|
1273
|
+
return Decoration.set(decorations, true);
|
|
1274
|
+
}
|
|
1275
|
+
var searchField = StateField.define({
|
|
1276
|
+
create() {
|
|
1277
|
+
return {
|
|
1278
|
+
query: "",
|
|
1279
|
+
selectedMatchIndex: void 0,
|
|
1280
|
+
matches: [],
|
|
1281
|
+
decorations: Decoration.none
|
|
1282
|
+
};
|
|
1283
|
+
},
|
|
1284
|
+
update(state, tr) {
|
|
1285
|
+
let newState = state;
|
|
1286
|
+
for (const effect of tr.effects) {
|
|
1287
|
+
if (!effect.is(setSearchEffect)) {
|
|
1288
|
+
continue;
|
|
1289
|
+
}
|
|
1290
|
+
const { query, selectedMatchIndex } = effect.value;
|
|
1291
|
+
const matches = findMatches(tr.state.doc, query);
|
|
1292
|
+
const decorations = createDecorations(matches, selectedMatchIndex);
|
|
1293
|
+
newState = {
|
|
1294
|
+
query,
|
|
1295
|
+
selectedMatchIndex,
|
|
1296
|
+
matches,
|
|
1297
|
+
decorations
|
|
1298
|
+
};
|
|
1299
|
+
}
|
|
1300
|
+
if (tr.docChanged && newState.query) {
|
|
1301
|
+
const matches = findMatches(tr.state.doc, newState.query);
|
|
1302
|
+
const decorations = createDecorations(matches, newState.selectedMatchIndex);
|
|
1303
|
+
newState = {
|
|
1304
|
+
...newState,
|
|
1305
|
+
matches,
|
|
1306
|
+
decorations
|
|
1307
|
+
};
|
|
1308
|
+
}
|
|
1309
|
+
return newState;
|
|
1310
|
+
},
|
|
1311
|
+
provide: (field) => EditorView.decorations.from(field, (state) => state.decorations)
|
|
1312
|
+
});
|
|
1313
|
+
var searchViewPlugin = ViewPlugin.fromClass(
|
|
1314
|
+
class {
|
|
1315
|
+
lastSelectedIndex;
|
|
1316
|
+
update(update) {
|
|
1317
|
+
const state = update.state.field(searchField);
|
|
1318
|
+
if (state.selectedMatchIndex !== void 0 && state.selectedMatchIndex !== this.lastSelectedIndex) {
|
|
1319
|
+
const match2 = state.matches[state.selectedMatchIndex];
|
|
1320
|
+
if (match2) {
|
|
1321
|
+
requestAnimationFrame(() => {
|
|
1322
|
+
update.view.dispatch({
|
|
1323
|
+
effects: EditorView.scrollIntoView(match2.from, { y: "center" })
|
|
1324
|
+
});
|
|
1325
|
+
});
|
|
1326
|
+
}
|
|
1327
|
+
this.lastSelectedIndex = state.selectedMatchIndex;
|
|
1328
|
+
}
|
|
1329
|
+
if (state.selectedMatchIndex === void 0) {
|
|
1330
|
+
this.lastSelectedIndex = void 0;
|
|
1331
|
+
}
|
|
1332
|
+
}
|
|
1333
|
+
}
|
|
1334
|
+
);
|
|
1335
|
+
var searchTheme = EditorView.baseTheme({
|
|
1336
|
+
".cm-searchMatch": {
|
|
1337
|
+
backgroundColor: colors.blue[100]
|
|
1338
|
+
},
|
|
1339
|
+
".cm-searchMatch > span": {
|
|
1340
|
+
color: `${colors.blue[600]} !important`
|
|
1341
|
+
},
|
|
1342
|
+
".cm-searchMatch-selected": {
|
|
1343
|
+
backgroundColor: colors.blue[600]
|
|
1344
|
+
},
|
|
1345
|
+
".cm-searchMatch-selected > span": {
|
|
1346
|
+
color: `${colors.white} !important`
|
|
1347
|
+
}
|
|
1348
|
+
});
|
|
1349
|
+
function searchExtension() {
|
|
1350
|
+
return [searchField, searchViewPlugin, searchTheme];
|
|
1351
|
+
}
|
|
1352
|
+
function updateSearch(view, query, selectedMatchIndex) {
|
|
1353
|
+
view.dispatch({ effects: setSearchEffect.of({ query, selectedMatchIndex }) });
|
|
1354
|
+
}
|
|
1166
1355
|
var KbdSeparator = ({ className, ...props }) => /* @__PURE__ */ jsx("span", { ...props, className: cn("tw:text-xs tw:text-muted-foreground", className) });
|
|
1167
1356
|
var KbdGroup = ({ ref, className, children, separator, ...props }) => {
|
|
1168
1357
|
return /* @__PURE__ */ jsx(
|
|
@@ -1222,11 +1411,78 @@ Kbd.Group = KbdGroup;
|
|
|
1222
1411
|
Kbd.Separator = KbdSeparator;
|
|
1223
1412
|
var ToggleComponentHelp = () => /* @__PURE__ */ jsxs("div", { children: [
|
|
1224
1413
|
"Use ",
|
|
1225
|
-
/* @__PURE__ */ jsx(Kbd, { children: "Ctrl+/" }),
|
|
1226
|
-
" or ",
|
|
1227
|
-
/* @__PURE__ */ jsx(Kbd, { children: "Command+/" }),
|
|
1414
|
+
/* @__PURE__ */ jsx(Kbd, { children: isMac ? "Command+/" : "Ctrl+/" }),
|
|
1228
1415
|
" to toggle comments."
|
|
1229
1416
|
] });
|
|
1417
|
+
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);
|
|
1418
|
+
var crops = (style) => style.overflowX !== "visible" || style.overflowY !== "visible" || style.maskImage !== "none" || style.contentVisibility !== "visible" || /\b(paint|strict|content)\b/.test(style.contain);
|
|
1419
|
+
var findTooltipHost = (from) => {
|
|
1420
|
+
let outermostCropper = null;
|
|
1421
|
+
for (let node = from.parentElement; node && node !== document.body; node = node.parentElement) {
|
|
1422
|
+
const style = getComputedStyle(node);
|
|
1423
|
+
if (crops(style)) {
|
|
1424
|
+
outermostCropper = node;
|
|
1425
|
+
} else if (establishesFixedContainingBlock(style)) {
|
|
1426
|
+
return outermostCropper?.parentElement ?? node;
|
|
1427
|
+
}
|
|
1428
|
+
}
|
|
1429
|
+
return outermostCropper?.parentElement ?? null;
|
|
1430
|
+
};
|
|
1431
|
+
var tooltipsExtension = () => {
|
|
1432
|
+
const layer = new Compartment();
|
|
1433
|
+
return [
|
|
1434
|
+
layer.of(tooltips()),
|
|
1435
|
+
ViewPlugin.define((view) => {
|
|
1436
|
+
const scheduled = setTimeout(() => {
|
|
1437
|
+
if (!view.dom.isConnected) return;
|
|
1438
|
+
const parent = findTooltipHost(view.dom);
|
|
1439
|
+
if (parent) view.dispatch({ effects: layer.reconfigure(tooltips({ parent })) });
|
|
1440
|
+
});
|
|
1441
|
+
return { destroy: () => clearTimeout(scheduled) };
|
|
1442
|
+
})
|
|
1443
|
+
];
|
|
1444
|
+
};
|
|
1445
|
+
var useTooltipsExtension = () => useMemo(tooltipsExtension, []);
|
|
1446
|
+
|
|
1447
|
+
// src/components/CodeEditor/variableCompletion.ts
|
|
1448
|
+
var BOOST = 2e3;
|
|
1449
|
+
var NAME = String.raw`[\w-]*`;
|
|
1450
|
+
var AT_CARET = new RegExp(String.raw`(?:\{{1,2} ?)?${NAME}$`);
|
|
1451
|
+
var NAME_AT_END = new RegExp(`${NAME}$`);
|
|
1452
|
+
var OPENING = /\{{1,2} ?$/;
|
|
1453
|
+
var CLOSING = /^ ?\}{1,2}/;
|
|
1454
|
+
var completeVariables = (variables = []) => {
|
|
1455
|
+
if (variables.length === 0) return () => null;
|
|
1456
|
+
const options = variables.map(({ name, value }) => ({
|
|
1457
|
+
// The bare name: `SqlEditor` filters prefix-only while `CodeEditor` filters
|
|
1458
|
+
// fuzzily, and this is the only spelling both accept whether or not the caret
|
|
1459
|
+
// has typed the braces. The braces are `displayLabel`, which is what renders.
|
|
1460
|
+
label: name,
|
|
1461
|
+
displayLabel: `{{${name}}}`,
|
|
1462
|
+
detail: value,
|
|
1463
|
+
type: "variable",
|
|
1464
|
+
boost: BOOST,
|
|
1465
|
+
apply: (view, completion, from, to) => {
|
|
1466
|
+
const { doc } = view.state;
|
|
1467
|
+
const line = doc.lineAt(from);
|
|
1468
|
+
const insert = `{{${completion.label}}}`;
|
|
1469
|
+
const opening = OPENING.exec(doc.sliceString(line.from, from));
|
|
1470
|
+
const closing = CLOSING.exec(doc.sliceString(to, line.to));
|
|
1471
|
+
const start = opening ? from - opening[0].length : from;
|
|
1472
|
+
const end = closing ? to + closing[0].length : to;
|
|
1473
|
+
view.dispatch({
|
|
1474
|
+
changes: { from: start, to: end, insert },
|
|
1475
|
+
selection: { anchor: start + insert.length }
|
|
1476
|
+
});
|
|
1477
|
+
}
|
|
1478
|
+
}));
|
|
1479
|
+
return (context) => {
|
|
1480
|
+
const match2 = context.matchBefore(AT_CARET);
|
|
1481
|
+
if (!match2?.text) return null;
|
|
1482
|
+
const [name = ""] = NAME_AT_END.exec(match2.text) ?? [];
|
|
1483
|
+
return { from: match2.to - name.length, options };
|
|
1484
|
+
};
|
|
1485
|
+
};
|
|
1230
1486
|
var emptyArray = [];
|
|
1231
1487
|
var emptyObject = {};
|
|
1232
1488
|
var CodeEditor = ({
|
|
@@ -1236,8 +1492,12 @@ var CodeEditor = ({
|
|
|
1236
1492
|
mode = "application/json",
|
|
1237
1493
|
placeholder: placeholder$1 = "",
|
|
1238
1494
|
ariaLabel,
|
|
1495
|
+
searchQuery,
|
|
1496
|
+
searchMatchIndex,
|
|
1239
1497
|
readOnly = false,
|
|
1240
1498
|
autocompletions = emptyArray,
|
|
1499
|
+
completionSource,
|
|
1500
|
+
variables,
|
|
1241
1501
|
extraKeys = emptyObject,
|
|
1242
1502
|
withToggleCommentHint = false,
|
|
1243
1503
|
withAutofocus = false,
|
|
@@ -1252,6 +1512,26 @@ var CodeEditor = ({
|
|
|
1252
1512
|
className
|
|
1253
1513
|
}) => {
|
|
1254
1514
|
const handleChange = useStableCallback((next) => onChange?.(next));
|
|
1515
|
+
const withSearch = searchQuery !== void 0;
|
|
1516
|
+
const editorRef = useRef(null);
|
|
1517
|
+
useEffect(() => {
|
|
1518
|
+
const view = editorRef.current?.view;
|
|
1519
|
+
if (view && withSearch) updateSearch(view, searchQuery, searchMatchIndex);
|
|
1520
|
+
}, [withSearch, searchQuery, searchMatchIndex]);
|
|
1521
|
+
const handleCreateEditor = useStableCallback((view) => {
|
|
1522
|
+
if (searchQuery) updateSearch(view, searchQuery, searchMatchIndex);
|
|
1523
|
+
});
|
|
1524
|
+
const extraKeysRef = useRef(extraKeys);
|
|
1525
|
+
useLayoutEffect(() => {
|
|
1526
|
+
extraKeysRef.current = extraKeys;
|
|
1527
|
+
});
|
|
1528
|
+
const boundKeysSignature = Object.keys(extraKeys).join("\n");
|
|
1529
|
+
const boundKeys = useMemo(
|
|
1530
|
+
() => boundKeysSignature.split("\n").filter(Boolean),
|
|
1531
|
+
[boundKeysSignature]
|
|
1532
|
+
);
|
|
1533
|
+
const tooltipLayer = useTooltipsExtension();
|
|
1534
|
+
const variableCompletion = useMemo(() => completeVariables(variables), [variables]);
|
|
1255
1535
|
const customTheme = useThemedExtension("editor:syntax", buildCustomTheme);
|
|
1256
1536
|
const baseTheme = useThemedExtension(
|
|
1257
1537
|
"code-editor:chrome",
|
|
@@ -1263,31 +1543,43 @@ var CodeEditor = ({
|
|
|
1263
1543
|
shouldLintJson(mode, readOnly) ? linter(jsonParseLinter()) : [],
|
|
1264
1544
|
withLineWrapping ? EditorView.lineWrapping : [],
|
|
1265
1545
|
EditorState.languageData.of(() => [
|
|
1266
|
-
{ autocomplete: completeAny ? completeAnyWord : autocompletions }
|
|
1546
|
+
{ autocomplete: completionSource ?? (completeAny ? completeAnyWord : autocompletions) }
|
|
1267
1547
|
]),
|
|
1548
|
+
EditorState.languageData.of(() => [{ autocomplete: variableCompletion }]),
|
|
1268
1549
|
keymap.of(
|
|
1269
|
-
|
|
1550
|
+
boundKeys.map((key) => ({
|
|
1270
1551
|
key,
|
|
1271
1552
|
run: () => {
|
|
1272
|
-
|
|
1273
|
-
run();
|
|
1553
|
+
extraKeysRef.current[key]?.();
|
|
1274
1554
|
return true;
|
|
1275
1555
|
}
|
|
1276
1556
|
}))
|
|
1277
1557
|
),
|
|
1278
|
-
readOnly ? [] :
|
|
1558
|
+
readOnly ? [] : [
|
|
1559
|
+
// CodeMirror's own default. Gating it on the props hid completions the
|
|
1560
|
+
// *language* supplies — dbt's vocabulary, Snowflake's keywords — behind
|
|
1561
|
+
// a shortcut, and macOS leaves no usable one. With nothing to offer, no
|
|
1562
|
+
// popup opens anyway.
|
|
1563
|
+
autocompletion()
|
|
1564
|
+
],
|
|
1279
1565
|
ariaLabel ? EditorView.contentAttributes.of({ "aria-label": ariaLabel }) : [],
|
|
1566
|
+
withSearch ? searchExtension() : [],
|
|
1567
|
+
tooltipLayer,
|
|
1280
1568
|
baseTheme
|
|
1281
1569
|
];
|
|
1282
1570
|
}, [
|
|
1283
1571
|
autocompletions,
|
|
1572
|
+
completionSource,
|
|
1573
|
+
variableCompletion,
|
|
1284
1574
|
completeAny,
|
|
1285
|
-
|
|
1575
|
+
boundKeys,
|
|
1286
1576
|
mode,
|
|
1287
1577
|
readOnly,
|
|
1288
1578
|
withLineWrapping,
|
|
1289
1579
|
baseTheme,
|
|
1290
|
-
ariaLabel
|
|
1580
|
+
ariaLabel,
|
|
1581
|
+
withSearch,
|
|
1582
|
+
tooltipLayer
|
|
1291
1583
|
]);
|
|
1292
1584
|
const extensions = useMemo(() => {
|
|
1293
1585
|
return [
|
|
@@ -1306,9 +1598,12 @@ var CodeEditor = ({
|
|
|
1306
1598
|
const config = useMemo(() => {
|
|
1307
1599
|
return {
|
|
1308
1600
|
...buildBasicSetup(readOnly),
|
|
1309
|
-
lineNumbers: withLineNumbers
|
|
1601
|
+
lineNumbers: withLineNumbers,
|
|
1602
|
+
// Mod-f opens CodeMirror's own panel, which would run a second query and a
|
|
1603
|
+
// second match count over the same document beside the caller's input.
|
|
1604
|
+
...withSearch ? { searchKeymap: false } : null
|
|
1310
1605
|
};
|
|
1311
|
-
}, [withLineNumbers, readOnly]);
|
|
1606
|
+
}, [withLineNumbers, readOnly, withSearch]);
|
|
1312
1607
|
const additionalButtons = renderAdditionalButtons?.(value);
|
|
1313
1608
|
const hasToolbar = withCopyButton && !!value || !!additionalButtons;
|
|
1314
1609
|
return (
|
|
@@ -1331,6 +1626,8 @@ var CodeEditor = ({
|
|
|
1331
1626
|
/* @__PURE__ */ jsx(
|
|
1332
1627
|
CodeMirror,
|
|
1333
1628
|
{
|
|
1629
|
+
ref: editorRef,
|
|
1630
|
+
onCreateEditor: handleCreateEditor,
|
|
1334
1631
|
theme: customTheme,
|
|
1335
1632
|
value,
|
|
1336
1633
|
onChange: handleChange,
|