@saltcorn/builder 1.6.0-alpha.13 → 1.6.0-alpha.15
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/builder_bundle.js +3 -3
- package/package.json +2 -2
- package/src/components/elements/Action.js +8 -119
- package/src/components/elements/DropDownFilter.js +8 -1
- package/src/components/elements/HTMLCode.js +3 -1
- package/src/components/elements/MonacoEditor.js +120 -15
- package/src/components/elements/View.js +1 -0
- package/src/components/elements/ViewLink.js +1 -0
- package/src/components/elements/utils.js +117 -21
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@saltcorn/builder",
|
|
3
|
-
"version": "1.6.0-alpha.
|
|
3
|
+
"version": "1.6.0-alpha.15",
|
|
4
4
|
"description": "Drag and drop view builder for Saltcorn, open-source no-code platform",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"homepage": "https://saltcorn.com",
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"@fortawesome/free-solid-svg-icons": "5.15.2",
|
|
31
31
|
"@fortawesome/react-fontawesome": "0.1.14",
|
|
32
32
|
"@monaco-editor/react": "4.7.0",
|
|
33
|
-
"@saltcorn/common-code": "1.6.0-alpha.
|
|
33
|
+
"@saltcorn/common-code": "1.6.0-alpha.15",
|
|
34
34
|
"@tippyjs/react": "4.2.6",
|
|
35
35
|
"babel-jest": "^29.7.0",
|
|
36
36
|
"babel-loader": "9.2.1",
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
/*global notifyAlert, apply_showif*/
|
|
7
7
|
|
|
8
|
-
import React, { Fragment, useContext, useEffect
|
|
8
|
+
import React, { Fragment, useContext, useEffect } from "react";
|
|
9
9
|
import { useNode } from "@craftjs/core";
|
|
10
10
|
import useTranslation from "../../hooks/useTranslation";
|
|
11
11
|
import optionsCtx from "../context";
|
|
@@ -25,7 +25,7 @@ import {
|
|
|
25
25
|
} from "./utils";
|
|
26
26
|
import { ntimes } from "./Columns";
|
|
27
27
|
import { ArrayManager } from "./ArrayManager";
|
|
28
|
-
|
|
28
|
+
|
|
29
29
|
import Select from "react-select";
|
|
30
30
|
|
|
31
31
|
export /**
|
|
@@ -146,13 +146,6 @@ const ActionSettings = () => {
|
|
|
146
146
|
const options = useContext(optionsCtx);
|
|
147
147
|
const getCfgFields = (fv) => (options.actionConfigForms || {})[fv];
|
|
148
148
|
const cfgFields = getCfgFields(name);
|
|
149
|
-
const cfgFieldsForForm =
|
|
150
|
-
name === "run_js_code"
|
|
151
|
-
? (cfgFields || []).filter((f) => f.name !== "code")
|
|
152
|
-
: cfgFields;
|
|
153
|
-
|
|
154
|
-
const runJsCodeModalOnly = false;
|
|
155
|
-
const [codeModalOpen, setCodeModalOpen] = useState(false);
|
|
156
149
|
const setAProp = setAPropGen(setProp);
|
|
157
150
|
const use_setting_action_n =
|
|
158
151
|
setting_action_n || setting_action_n === 0 ? setting_action_n : 0;
|
|
@@ -457,116 +450,12 @@ const ActionSettings = () => {
|
|
|
457
450
|
) : null}
|
|
458
451
|
</Fragment>
|
|
459
452
|
) : cfgFields ? (
|
|
460
|
-
<
|
|
461
|
-
{
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
className="btn btn-secondary btn-sm"
|
|
467
|
-
onClick={() => setCodeModalOpen(true)}
|
|
468
|
-
>
|
|
469
|
-
{t("Open Code Popup")}
|
|
470
|
-
</button>
|
|
471
|
-
</div>
|
|
472
|
-
) : null}
|
|
473
|
-
{name === "run_js_code" && !runJsCodeModalOnly ? (
|
|
474
|
-
<Fragment>
|
|
475
|
-
<ConfigForm
|
|
476
|
-
fields={(cfgFields || []).filter((f) => f.name === "code")}
|
|
477
|
-
configuration={configuration}
|
|
478
|
-
setProp={setProp}
|
|
479
|
-
node={node}
|
|
480
|
-
openPopup={() => setCodeModalOpen(true)}
|
|
481
|
-
/>
|
|
482
|
-
{/* <div className="builder-config-field mt-2" data-field-name="code-modal-trigger">
|
|
483
|
-
<button
|
|
484
|
-
type="button"
|
|
485
|
-
className="btn btn-secondary btn-sm"
|
|
486
|
-
onClick={() => setCodeModalOpen(true)}
|
|
487
|
-
>
|
|
488
|
-
{t("Open Code Popup")}
|
|
489
|
-
</button>
|
|
490
|
-
</div> */}
|
|
491
|
-
<ConfigForm
|
|
492
|
-
fields={cfgFieldsForForm}
|
|
493
|
-
configuration={configuration}
|
|
494
|
-
setProp={setProp}
|
|
495
|
-
node={node}
|
|
496
|
-
/>
|
|
497
|
-
</Fragment>
|
|
498
|
-
) : (
|
|
499
|
-
<ConfigForm
|
|
500
|
-
fields={runJsCodeModalOnly ? cfgFieldsForForm : cfgFields}
|
|
501
|
-
configuration={configuration}
|
|
502
|
-
setProp={setProp}
|
|
503
|
-
node={node}
|
|
504
|
-
/>
|
|
505
|
-
)}
|
|
506
|
-
{name === "run_js_code" && codeModalOpen ? (
|
|
507
|
-
<div
|
|
508
|
-
className={`modal fade ${codeModalOpen ? "show" : ""}`}
|
|
509
|
-
style={{
|
|
510
|
-
display: codeModalOpen ? "block" : "none",
|
|
511
|
-
zIndex: 1055,
|
|
512
|
-
}}
|
|
513
|
-
tabIndex={-1}
|
|
514
|
-
role="dialog"
|
|
515
|
-
aria-labelledby="codeModalLabel"
|
|
516
|
-
aria-hidden={!codeModalOpen}
|
|
517
|
-
>
|
|
518
|
-
<div
|
|
519
|
-
className="modal-backdrop fade show"
|
|
520
|
-
style={{ zIndex: 1050 }}
|
|
521
|
-
onClick={() => setCodeModalOpen(false)}
|
|
522
|
-
aria-hidden="true"
|
|
523
|
-
/>
|
|
524
|
-
<div
|
|
525
|
-
className="modal-dialog modal-dialog-centered modal-lg"
|
|
526
|
-
role="document"
|
|
527
|
-
style={{ zIndex: 1060 }}
|
|
528
|
-
onClick={(e) => e.stopPropagation()}
|
|
529
|
-
>
|
|
530
|
-
<div className="modal-content code-modal">
|
|
531
|
-
<div className="modal-header">
|
|
532
|
-
<h5 className="modal-title" id="codeModalLabel">
|
|
533
|
-
{t("Code")}
|
|
534
|
-
</h5>
|
|
535
|
-
<button
|
|
536
|
-
type="button"
|
|
537
|
-
className="btn-close"
|
|
538
|
-
aria-label="Close"
|
|
539
|
-
onClick={() => setCodeModalOpen(false)}
|
|
540
|
-
/>
|
|
541
|
-
</div>
|
|
542
|
-
<div className="modal-body">
|
|
543
|
-
<MultiLineCodeEditor
|
|
544
|
-
setProp={setProp}
|
|
545
|
-
value={configuration?.code ?? ""}
|
|
546
|
-
onChange={(code) =>
|
|
547
|
-
setProp((prop) => {
|
|
548
|
-
if (!prop.configuration)
|
|
549
|
-
prop.configuration = {};
|
|
550
|
-
prop.configuration.code = code;
|
|
551
|
-
})
|
|
552
|
-
}
|
|
553
|
-
isModalEditor
|
|
554
|
-
/>
|
|
555
|
-
</div>
|
|
556
|
-
<div className="modal-footer">
|
|
557
|
-
<button
|
|
558
|
-
type="button"
|
|
559
|
-
className="btn btn-secondary"
|
|
560
|
-
onClick={() => setCodeModalOpen(false)}
|
|
561
|
-
>
|
|
562
|
-
{t("Close")}
|
|
563
|
-
</button>
|
|
564
|
-
</div>
|
|
565
|
-
</div>
|
|
566
|
-
</div>
|
|
567
|
-
</div>
|
|
568
|
-
) : null}
|
|
569
|
-
</Fragment>
|
|
453
|
+
<ConfigForm
|
|
454
|
+
fields={cfgFields}
|
|
455
|
+
configuration={configuration}
|
|
456
|
+
setProp={setProp}
|
|
457
|
+
node={node}
|
|
458
|
+
/>
|
|
570
459
|
) : null}
|
|
571
460
|
{cfg_link ? (
|
|
572
461
|
<a className="d-block mt-2" target="_blank" href={cfg_link}>
|
|
@@ -4,7 +4,9 @@
|
|
|
4
4
|
* @subcategory components / elements
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
/* globals validate_bool_expression_elem */
|
|
8
|
+
|
|
9
|
+
import React, { Fragment, useState, useContext, useEffect, useRef } from "react";
|
|
8
10
|
import { useNode } from "@craftjs/core";
|
|
9
11
|
import useTranslation from "../../hooks/useTranslation";
|
|
10
12
|
import optionsCtx from "../context";
|
|
@@ -73,6 +75,7 @@ const DropDownFilterSettings = () => {
|
|
|
73
75
|
}));
|
|
74
76
|
const options = useContext(optionsCtx);
|
|
75
77
|
const setAProp = setAPropGen(setProp);
|
|
78
|
+
const editorRef = useRef(null);
|
|
76
79
|
let select_all_options;
|
|
77
80
|
const field = options.fields.find((f) => f.name === name);
|
|
78
81
|
if (field?.type === "String" && field.attributes?.options)
|
|
@@ -121,9 +124,13 @@ const DropDownFilterSettings = () => {
|
|
|
121
124
|
</td>
|
|
122
125
|
<td>
|
|
123
126
|
<SingleLineEditor
|
|
127
|
+
ref={editorRef}
|
|
124
128
|
value={where}
|
|
125
129
|
setProp={setProp}
|
|
126
130
|
propKey="where"
|
|
131
|
+
onInput={(value) =>
|
|
132
|
+
validate_bool_expression_elem(value, editorRef.current)
|
|
133
|
+
}
|
|
127
134
|
/>
|
|
128
135
|
</td>
|
|
129
136
|
</tr>
|
|
@@ -45,7 +45,9 @@ const fields = (mode) => {
|
|
|
45
45
|
{
|
|
46
46
|
label: "HTML Code",
|
|
47
47
|
name: "text",
|
|
48
|
-
|
|
48
|
+
input_type: "code",
|
|
49
|
+
type: "code",
|
|
50
|
+
attributes: { mode: "text/html" },
|
|
49
51
|
segment_name: "contents",
|
|
50
52
|
onSave: (segment, node_props) => {
|
|
51
53
|
const div = document.createElement("div");
|
|
@@ -3,6 +3,24 @@ import optionsCtx from "../context";
|
|
|
3
3
|
|
|
4
4
|
import Editor, { useMonaco } from "@monaco-editor/react";
|
|
5
5
|
|
|
6
|
+
export const mimeToMonacoLanguage = (mode) => {
|
|
7
|
+
if (!mode) return "typescript";
|
|
8
|
+
const map = {
|
|
9
|
+
"text/javascript": "typescript",
|
|
10
|
+
"application/javascript": "typescript",
|
|
11
|
+
"text/html": "html",
|
|
12
|
+
"text/css": "css",
|
|
13
|
+
"application/json": "json",
|
|
14
|
+
"text/x-sql": "sql",
|
|
15
|
+
"text/x-python": "python",
|
|
16
|
+
"text/x-yaml": "yaml",
|
|
17
|
+
"text/xml": "xml",
|
|
18
|
+
"text/x-markdown": "markdown",
|
|
19
|
+
"text/typescript": "typescript",
|
|
20
|
+
};
|
|
21
|
+
return map[mode] || mode;
|
|
22
|
+
};
|
|
23
|
+
|
|
6
24
|
const setMonacoLanguage = async (monaco, options, isStatements) => {
|
|
7
25
|
monaco.languages.typescript.typescriptDefaults.setCompilerOptions({
|
|
8
26
|
noLib: true,
|
|
@@ -27,33 +45,108 @@ const setMonacoLanguage = async (monaco, options, isStatements) => {
|
|
|
27
45
|
});
|
|
28
46
|
};
|
|
29
47
|
|
|
48
|
+
// add hidden-prefix
|
|
49
|
+
// line 1 holds the typed prefix, the user edits from line 2 onwards
|
|
50
|
+
const setupVirtualPrefix = (editor, monaco) => {
|
|
51
|
+
const model = editor.getModel();
|
|
52
|
+
editor.setHiddenAreas([{ startLineNumber: 1, endLineNumber: 1 }]);
|
|
53
|
+
editor.onDidChangeCursorPosition((e) => {
|
|
54
|
+
if (e.position.lineNumber < 2)
|
|
55
|
+
editor.setPosition({ lineNumber: 2, column: 1 });
|
|
56
|
+
});
|
|
57
|
+
editor.onKeyDown((e) => {
|
|
58
|
+
const pos = editor.getPosition();
|
|
59
|
+
if (
|
|
60
|
+
pos.lineNumber === 2 &&
|
|
61
|
+
pos.column === 1 &&
|
|
62
|
+
e.keyCode === monaco.KeyCode.Backspace
|
|
63
|
+
) {
|
|
64
|
+
e.preventDefault();
|
|
65
|
+
e.stopPropagation();
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
// copy without the prefix line
|
|
69
|
+
editor.addAction({
|
|
70
|
+
id: "copy-editable-only",
|
|
71
|
+
label: "Copy Only User Content",
|
|
72
|
+
keybindings: [monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyC],
|
|
73
|
+
run: (ed) => {
|
|
74
|
+
const selection = ed.getSelection();
|
|
75
|
+
if (selection.isEmpty()) return;
|
|
76
|
+
const safeSelection = selection.intersectRanges(
|
|
77
|
+
new monaco.Range(
|
|
78
|
+
2,
|
|
79
|
+
1,
|
|
80
|
+
model.getLineCount(),
|
|
81
|
+
model.getLineMaxColumn(model.getLineCount())
|
|
82
|
+
)
|
|
83
|
+
);
|
|
84
|
+
if (safeSelection)
|
|
85
|
+
navigator.clipboard.writeText(model.getValueInRange(safeSelection));
|
|
86
|
+
},
|
|
87
|
+
});
|
|
88
|
+
// select all without the prefix line
|
|
89
|
+
editor.addAction({
|
|
90
|
+
id: "select-editable-only",
|
|
91
|
+
label: "Select Only User Content",
|
|
92
|
+
keybindings: [monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyA],
|
|
93
|
+
run: (ed) => {
|
|
94
|
+
ed.setSelection(
|
|
95
|
+
new monaco.Range(
|
|
96
|
+
2,
|
|
97
|
+
1,
|
|
98
|
+
model.getLineCount(),
|
|
99
|
+
model.getLineMaxColumn(model.getLineCount())
|
|
100
|
+
)
|
|
101
|
+
);
|
|
102
|
+
},
|
|
103
|
+
});
|
|
104
|
+
};
|
|
105
|
+
|
|
30
106
|
export const SingleLineEditor = React.forwardRef(
|
|
31
|
-
(
|
|
107
|
+
(
|
|
108
|
+
{ setProp, value, propKey, onChange, onInput, className, stateExpr },
|
|
109
|
+
ref
|
|
110
|
+
) => {
|
|
32
111
|
const options = React.useContext(optionsCtx);
|
|
112
|
+
const activePrefix = stateExpr ? "const _: Row =" : null;
|
|
33
113
|
|
|
34
114
|
const handleEditorWillMount = (monaco) => {
|
|
35
115
|
setMonacoLanguage(monaco, options, false);
|
|
36
116
|
};
|
|
37
117
|
|
|
38
118
|
const handleEditorDidMount = (editor, monaco) => {
|
|
119
|
+
if (activePrefix) {
|
|
120
|
+
setupVirtualPrefix(editor, monaco);
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
39
123
|
if (!onInput) return;
|
|
40
124
|
|
|
41
125
|
editor.onDidChangeModelContent(() => {
|
|
42
|
-
|
|
43
|
-
onInput(value);
|
|
126
|
+
onInput(editor.getValue());
|
|
44
127
|
});
|
|
45
128
|
};
|
|
46
129
|
|
|
130
|
+
const isEmpty = !value || value.trim() === "";
|
|
131
|
+
const editorValue = activePrefix
|
|
132
|
+
? `${isEmpty ? "//" : ""} ${activePrefix}\n${value || ""}`
|
|
133
|
+
: value;
|
|
134
|
+
|
|
47
135
|
return (
|
|
48
136
|
<div ref={ref} className="form-control p-0 pt-1">
|
|
49
137
|
<Editor
|
|
50
138
|
placeholder={"sdfffsd"}
|
|
51
139
|
className={className || ""}
|
|
52
140
|
height="22px"
|
|
53
|
-
value={
|
|
54
|
-
onChange={(
|
|
55
|
-
|
|
56
|
-
|
|
141
|
+
value={editorValue}
|
|
142
|
+
onChange={(fullValue) => {
|
|
143
|
+
const userValue = activePrefix
|
|
144
|
+
? (fullValue || "").substring((fullValue || "").indexOf("\n") + 1)
|
|
145
|
+
: fullValue;
|
|
146
|
+
onChange && onChange(userValue);
|
|
147
|
+
setProp &&
|
|
148
|
+
propKey &&
|
|
149
|
+
setProp((prop) => (prop[propKey] = userValue));
|
|
57
150
|
}}
|
|
58
151
|
defaultLanguage="typescript"
|
|
59
152
|
//onMount={handleEditorDidMount}
|
|
@@ -68,25 +161,37 @@ export const SingleLineEditor = React.forwardRef(
|
|
|
68
161
|
}
|
|
69
162
|
);
|
|
70
163
|
|
|
71
|
-
export const MultiLineCodeEditor = ({ setProp, value, onChange, isModalEditor = false }) => {
|
|
164
|
+
export const MultiLineCodeEditor = ({ setProp, value, onChange, isModalEditor = false, mode }) => {
|
|
72
165
|
const options = React.useContext(optionsCtx);
|
|
166
|
+
const resolvedLanguage = mimeToMonacoLanguage(mode);
|
|
167
|
+
const useTypeScriptSetup = resolvedLanguage === "typescript" || resolvedLanguage === "javascript";
|
|
73
168
|
|
|
74
169
|
const handleEditorWillMount = (monaco) => {
|
|
75
|
-
|
|
170
|
+
if (useTypeScriptSetup) {
|
|
171
|
+
setMonacoLanguage(monaco, options, true);
|
|
172
|
+
}
|
|
76
173
|
};
|
|
174
|
+
|
|
175
|
+
const handleEditorDidMount = (editor, monaco) => {
|
|
176
|
+
if (!useTypeScriptSetup) {
|
|
177
|
+
const model = editor.getModel();
|
|
178
|
+
if (model) {
|
|
179
|
+
monaco.editor.setModelLanguage(model, resolvedLanguage);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
|
|
77
184
|
return (
|
|
78
185
|
<div className="form-control p-0 pt-2">
|
|
79
186
|
<Editor
|
|
80
187
|
height={isModalEditor ? "100%" : "150px"}
|
|
81
188
|
value={value}
|
|
82
189
|
onChange={onChange}
|
|
83
|
-
defaultLanguage=
|
|
84
|
-
//onMount={handleEditorDidMount}
|
|
85
|
-
//beforeMount={handleEditorWillMount}
|
|
190
|
+
defaultLanguage={resolvedLanguage}
|
|
86
191
|
options={multiLineEditorOptions}
|
|
87
|
-
//theme="myCoolTheme"
|
|
88
192
|
beforeMount={handleEditorWillMount}
|
|
89
|
-
|
|
193
|
+
onMount={handleEditorDidMount}
|
|
194
|
+
className={isModalEditor ? "code-modal-form" : ""}
|
|
90
195
|
/>
|
|
91
196
|
</div>
|
|
92
197
|
);
|
|
@@ -94,7 +199,7 @@ export const MultiLineCodeEditor = ({ setProp, value, onChange, isModalEditor =
|
|
|
94
199
|
|
|
95
200
|
const multiLineEditorOptions = {
|
|
96
201
|
fontSize: "14px",
|
|
97
|
-
minHeight:"80vh",
|
|
202
|
+
minHeight: "80vh",
|
|
98
203
|
fontWeight: "normal",
|
|
99
204
|
wordWrap: "off",
|
|
100
205
|
lineNumbers: "off",
|
|
@@ -932,6 +932,95 @@ const ColorInput = ({ value, onChange }) =>
|
|
|
932
932
|
</button>
|
|
933
933
|
);
|
|
934
934
|
|
|
935
|
+
const CodeFieldWithModal = ({ value, onChange, setProp, mode, label, hideLabel }) => {
|
|
936
|
+
const [modalOpen, setModalOpen] = useState(false);
|
|
937
|
+
const { t } = useTranslation();
|
|
938
|
+
return (
|
|
939
|
+
<Fragment>
|
|
940
|
+
{!hideLabel && (
|
|
941
|
+
<label>
|
|
942
|
+
{t(label)}{" "}
|
|
943
|
+
<i
|
|
944
|
+
className="fas fa-external-link-alt ms-1"
|
|
945
|
+
style={{ cursor: "pointer" }}
|
|
946
|
+
onClick={() => setModalOpen(true)}
|
|
947
|
+
title={t("Open code popup")}
|
|
948
|
+
></i>
|
|
949
|
+
</label>
|
|
950
|
+
)}
|
|
951
|
+
{hideLabel && (
|
|
952
|
+
<i
|
|
953
|
+
className="fas fa-external-link-alt ms-1"
|
|
954
|
+
style={{ cursor: "pointer" }}
|
|
955
|
+
onClick={() => setModalOpen(true)}
|
|
956
|
+
title={t("Open code popup")}
|
|
957
|
+
></i>
|
|
958
|
+
)}
|
|
959
|
+
<MultiLineCodeEditor
|
|
960
|
+
setProp={setProp}
|
|
961
|
+
value={value}
|
|
962
|
+
onChange={onChange}
|
|
963
|
+
mode={mode}
|
|
964
|
+
/>
|
|
965
|
+
{modalOpen ? (
|
|
966
|
+
<div
|
|
967
|
+
className={`modal fade show`}
|
|
968
|
+
style={{ display: "block", zIndex: 1055 }}
|
|
969
|
+
tabIndex={-1}
|
|
970
|
+
role="dialog"
|
|
971
|
+
aria-labelledby="codeModalLabel"
|
|
972
|
+
aria-hidden={false}
|
|
973
|
+
>
|
|
974
|
+
<div
|
|
975
|
+
className="modal-backdrop fade show"
|
|
976
|
+
style={{ zIndex: 1050 }}
|
|
977
|
+
onClick={() => setModalOpen(false)}
|
|
978
|
+
aria-hidden="true"
|
|
979
|
+
/>
|
|
980
|
+
<div
|
|
981
|
+
className="modal-dialog modal-dialog-centered modal-lg"
|
|
982
|
+
role="document"
|
|
983
|
+
style={{ zIndex: 1060 }}
|
|
984
|
+
onClick={(e) => e.stopPropagation()}
|
|
985
|
+
>
|
|
986
|
+
<div className="modal-content code-modal">
|
|
987
|
+
<div className="modal-header">
|
|
988
|
+
<h5 className="modal-title" id="codeModalLabel">
|
|
989
|
+
{t(label)}
|
|
990
|
+
</h5>
|
|
991
|
+
<button
|
|
992
|
+
type="button"
|
|
993
|
+
className="btn-close"
|
|
994
|
+
aria-label="Close"
|
|
995
|
+
onClick={() => setModalOpen(false)}
|
|
996
|
+
/>
|
|
997
|
+
</div>
|
|
998
|
+
<div className="modal-body">
|
|
999
|
+
<MultiLineCodeEditor
|
|
1000
|
+
setProp={setProp}
|
|
1001
|
+
value={value}
|
|
1002
|
+
onChange={onChange}
|
|
1003
|
+
isModalEditor
|
|
1004
|
+
mode={mode}
|
|
1005
|
+
/>
|
|
1006
|
+
</div>
|
|
1007
|
+
<div className="modal-footer">
|
|
1008
|
+
<button
|
|
1009
|
+
type="button"
|
|
1010
|
+
className="btn btn-secondary"
|
|
1011
|
+
onClick={() => setModalOpen(false)}
|
|
1012
|
+
>
|
|
1013
|
+
{t("Close")}
|
|
1014
|
+
</button>
|
|
1015
|
+
</div>
|
|
1016
|
+
</div>
|
|
1017
|
+
</div>
|
|
1018
|
+
</div>
|
|
1019
|
+
) : null}
|
|
1020
|
+
</Fragment>
|
|
1021
|
+
);
|
|
1022
|
+
};
|
|
1023
|
+
|
|
935
1024
|
export /**
|
|
936
1025
|
* @param {object} props
|
|
937
1026
|
* @param {object[]} props.fields
|
|
@@ -953,7 +1042,6 @@ const ConfigForm = ({
|
|
|
953
1042
|
onChange,
|
|
954
1043
|
tableName,
|
|
955
1044
|
fieldName,
|
|
956
|
-
openPopup
|
|
957
1045
|
}) => (
|
|
958
1046
|
<div className="form-namespace">
|
|
959
1047
|
{fields.map((f, ix) => {
|
|
@@ -968,7 +1056,7 @@ const ConfigForm = ({
|
|
|
968
1056
|
}
|
|
969
1057
|
return (
|
|
970
1058
|
<div key={ix} className="builder-config-field" data-field-name={f.name}>
|
|
971
|
-
{!isCheckbox(f) ? (
|
|
1059
|
+
{!isCheckbox(f) && f.input_type !== "code" ? (
|
|
972
1060
|
<label>
|
|
973
1061
|
{f.label || f.name}
|
|
974
1062
|
{f.help ? (
|
|
@@ -978,7 +1066,7 @@ const ConfigForm = ({
|
|
|
978
1066
|
table_name={tableName}
|
|
979
1067
|
/>
|
|
980
1068
|
) : null}
|
|
981
|
-
{" "}
|
|
1069
|
+
{" "}
|
|
982
1070
|
</label>
|
|
983
1071
|
) : null}
|
|
984
1072
|
<ConfigField
|
|
@@ -1233,25 +1321,33 @@ const ConfigField = ({
|
|
|
1233
1321
|
onChange={(e) => e.target && myOnChange(e.target.value)}
|
|
1234
1322
|
/>
|
|
1235
1323
|
),
|
|
1236
|
-
code: () =>
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1324
|
+
code: () => {
|
|
1325
|
+
if (
|
|
1326
|
+
field?.attributes?.expression_type === "row" ||
|
|
1327
|
+
field?.attributes?.expression_type === "query"
|
|
1328
|
+
) {
|
|
1329
|
+
return (
|
|
1330
|
+
<textarea
|
|
1331
|
+
rows="6"
|
|
1332
|
+
type="text"
|
|
1333
|
+
className={`field-${field?.name} form-control`}
|
|
1334
|
+
value={value}
|
|
1335
|
+
name={field?.name}
|
|
1336
|
+
onChange={(e) => e.target && myOnChange(e.target.value)}
|
|
1337
|
+
spellCheck={false}
|
|
1338
|
+
/>
|
|
1339
|
+
);
|
|
1340
|
+
}
|
|
1341
|
+
return (
|
|
1342
|
+
<CodeFieldWithModal
|
|
1251
1343
|
value={value}
|
|
1252
1344
|
onChange={myOnChange}
|
|
1345
|
+
setProp={setProp}
|
|
1346
|
+
mode={field?.attributes?.mode}
|
|
1347
|
+
label={field?.label || field?.name || "Code"}
|
|
1253
1348
|
/>
|
|
1254
|
-
)
|
|
1349
|
+
);
|
|
1350
|
+
},
|
|
1255
1351
|
select: () => {
|
|
1256
1352
|
if (field.class?.includes?.("selectizable")) {
|
|
1257
1353
|
const seloptions = field.options.map((o, ix) =>
|
|
@@ -1500,7 +1596,7 @@ const SettingsRow = ({
|
|
|
1500
1596
|
valuePostfix,
|
|
1501
1597
|
}) => {
|
|
1502
1598
|
const { t } = useTranslation();
|
|
1503
|
-
const fullWidth = ["String", "Bool", "textarea"].includes(field.type);
|
|
1599
|
+
const fullWidth = ["String", "Bool", "textarea"].includes(field.type) || field.input_type === "code";
|
|
1504
1600
|
const needLabel = field.type !== "Bool";
|
|
1505
1601
|
const inner = field.canBeFormula ? (
|
|
1506
1602
|
<OrFormula
|
|
@@ -1530,7 +1626,7 @@ const SettingsRow = ({
|
|
|
1530
1626
|
<tr>
|
|
1531
1627
|
{fullWidth ? (
|
|
1532
1628
|
<td colSpan="2">
|
|
1533
|
-
{needLabel && <label>{field.label}</label>}
|
|
1629
|
+
{needLabel && field.input_type !== "code" && <label>{field.label}</label>}
|
|
1534
1630
|
{inner}
|
|
1535
1631
|
{field.sublabel ? (
|
|
1536
1632
|
<i
|