@saltcorn/builder 0.9.2 → 0.9.3-beta.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/babel.config.js +6 -0
- package/dist/builder_bundle.js +10 -10
- package/package.json +10 -2
- package/src/components/Toolbox.js +4 -10
- package/src/components/elements/RelationBadges.js +14 -11
- package/src/components/elements/RelationOnDemandPicker.js +212 -0
- package/src/components/elements/View.js +82 -49
- package/src/components/elements/ViewLink.js +47 -24
- package/src/components/elements/utils.js +35 -84
- package/tests/relations_finder.test.js +275 -0
- package/tests/test_data.js +163 -0
- package/src/components/elements/RelationPicker.js +0 -273
package/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@saltcorn/builder",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.3-beta.0",
|
|
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",
|
|
7
7
|
"scripts": {
|
|
8
8
|
"build": "webpack --mode production",
|
|
9
9
|
"builddev": "webpack --mode none",
|
|
10
|
-
"test": "
|
|
10
|
+
"test": "jest tests --runInBand",
|
|
11
11
|
"tsc": "echo \"Error: no TypeScript support yet\"",
|
|
12
12
|
"clean": "echo \"Error: no TypeScript support yet\""
|
|
13
13
|
},
|
|
@@ -26,14 +26,19 @@
|
|
|
26
26
|
"@fortawesome/free-regular-svg-icons": "5.15.2",
|
|
27
27
|
"@fortawesome/free-solid-svg-icons": "5.15.2",
|
|
28
28
|
"@fortawesome/react-fontawesome": "0.1.14",
|
|
29
|
+
"babel-jest": "^29.7.0",
|
|
29
30
|
"babel-loader": "8.1.0",
|
|
30
31
|
"ckeditor4-react": "1.4.2",
|
|
31
32
|
"classnames": "2.2.6",
|
|
33
|
+
"jest-environment-jsdom": "29.7.0",
|
|
34
|
+
"jest": "^29.7.0",
|
|
35
|
+
"jsdom": "16.4.0",
|
|
32
36
|
"prop-types": "15.7.2",
|
|
33
37
|
"react": "16.13.1",
|
|
34
38
|
"react-bootstrap-icons": "1.5.0",
|
|
35
39
|
"react-contenteditable": "3.3.5",
|
|
36
40
|
"react-dom": "16.13.1",
|
|
41
|
+
"react-test-renderer": "16.13.1",
|
|
37
42
|
"react-transition-group": "4.4.1",
|
|
38
43
|
"@tippyjs/react": "4.2.6",
|
|
39
44
|
"webpack": "5.68.0",
|
|
@@ -51,5 +56,8 @@
|
|
|
51
56
|
"overrides": {
|
|
52
57
|
"immer": "9.0.6",
|
|
53
58
|
"glob-parent": "^5.1.2"
|
|
59
|
+
},
|
|
60
|
+
"jest": {
|
|
61
|
+
"testEnvironment": "jsdom"
|
|
54
62
|
}
|
|
55
63
|
}
|
|
@@ -257,13 +257,9 @@ const ViewElem = ({ connectors, views }) => (
|
|
|
257
257
|
icon="fas fa-eye"
|
|
258
258
|
title="Embed a view"
|
|
259
259
|
label="View"
|
|
260
|
-
disable={views.length
|
|
260
|
+
disable={views.length < 2}
|
|
261
261
|
>
|
|
262
|
-
<View
|
|
263
|
-
name={"not_assigned"}
|
|
264
|
-
state={"shared"}
|
|
265
|
-
view={views.length > 0 ? views[0].name : "view"}
|
|
266
|
-
/>
|
|
262
|
+
<View name={"not_assigned"} state={"shared"} view={views[0].name} />
|
|
267
263
|
</WrapElem>
|
|
268
264
|
);
|
|
269
265
|
/**
|
|
@@ -429,12 +425,10 @@ const ViewLinkElem = ({ connectors, options }) => (
|
|
|
429
425
|
icons={["fas fa-eye", "fas fa-link"]}
|
|
430
426
|
title="Link to a view"
|
|
431
427
|
label="ViewLink"
|
|
432
|
-
disable={options.
|
|
428
|
+
disable={options.views.length < 2}
|
|
433
429
|
>
|
|
434
430
|
<ViewLink
|
|
435
|
-
name={
|
|
436
|
-
options.link_view_opts.length > 0 ? options.link_view_opts[0].name : ""
|
|
437
|
-
}
|
|
431
|
+
name={options.views.length > 0 ? options.views[0].name : ""}
|
|
438
432
|
block={false}
|
|
439
433
|
minRole={100}
|
|
440
434
|
label={""}
|
|
@@ -1,9 +1,5 @@
|
|
|
1
|
-
import React
|
|
2
|
-
import {
|
|
3
|
-
parseRelationPath,
|
|
4
|
-
parseLegacyRelation,
|
|
5
|
-
removeWhitespaces,
|
|
6
|
-
} from "./utils";
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { removeWhitespaces } from "./utils";
|
|
7
3
|
|
|
8
4
|
const buildBadgeCfgs = (parsed, parentTbl) => {
|
|
9
5
|
const result = [];
|
|
@@ -13,8 +9,8 @@ const buildBadgeCfgs = (parsed, parentTbl) => {
|
|
|
13
9
|
if (currentCfg) result.push(currentCfg);
|
|
14
10
|
currentCfg = { up: key, table };
|
|
15
11
|
} else {
|
|
16
|
-
if (!currentCfg) result.push({ down: key, table: parentTbl });
|
|
17
|
-
else {
|
|
12
|
+
if (!currentCfg && key) result.push({ down: key, table: parentTbl });
|
|
13
|
+
else if (currentCfg) {
|
|
18
14
|
currentCfg.down = key;
|
|
19
15
|
result.push(currentCfg);
|
|
20
16
|
}
|
|
@@ -63,9 +59,15 @@ const buildBadge = ({ up, table, down }, index) => {
|
|
|
63
59
|
);
|
|
64
60
|
};
|
|
65
61
|
|
|
66
|
-
export const RelationBadges = ({
|
|
62
|
+
export const RelationBadges = ({
|
|
63
|
+
view,
|
|
64
|
+
relation,
|
|
65
|
+
parentTbl,
|
|
66
|
+
tableNameCache,
|
|
67
|
+
}) => {
|
|
67
68
|
if (relation) {
|
|
68
|
-
const parsed = parseRelationPath(relation,
|
|
69
|
+
const parsed = relationHelpers.parseRelationPath(relation, tableNameCache);
|
|
70
|
+
|
|
69
71
|
return (
|
|
70
72
|
<div className="overflow-scroll">
|
|
71
73
|
{parsed.length > 0
|
|
@@ -74,8 +76,9 @@ export const RelationBadges = ({ view, relation, parentTbl, fk_options }) => {
|
|
|
74
76
|
</div>
|
|
75
77
|
);
|
|
76
78
|
} else {
|
|
79
|
+
if (!view) return buildBadge({ table: "invalid relation" }, 0);
|
|
77
80
|
const [prefix, rest] = view.split(":");
|
|
78
|
-
const parsed = parseLegacyRelation(prefix, rest, parentTbl);
|
|
81
|
+
const parsed = relationHelpers.parseLegacyRelation(prefix, rest, parentTbl);
|
|
79
82
|
if (parsed.length === 0)
|
|
80
83
|
return buildBadge({ table: "invalid relation" }, 0);
|
|
81
84
|
else if (
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { removeWhitespaces, rand_ident } from "./utils";
|
|
3
|
+
import ReactDOM from "react-dom";
|
|
4
|
+
|
|
5
|
+
const maxLevelDefault = 10;
|
|
6
|
+
|
|
7
|
+
const keyLabel = (key, type) =>
|
|
8
|
+
type === "fk" ? `${key.name}` : `${key.name} (from ${key.table})`;
|
|
9
|
+
|
|
10
|
+
const toggleLayers = (layer, maxLevel, additionalSelectors = []) => {
|
|
11
|
+
const selectors = [];
|
|
12
|
+
for (let i = layer; i < maxLevel; i++) {
|
|
13
|
+
selectors.push(`.dropdown_level_${i}.show`);
|
|
14
|
+
}
|
|
15
|
+
selectors.push(...additionalSelectors);
|
|
16
|
+
if (selectors.length) $(`${selectors.join(",")}`).dropdown("toggle");
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const setActiveClasses = (level, maxLevel, itemId) => {
|
|
20
|
+
const classes = [];
|
|
21
|
+
for (let i = level; i < maxLevel; i++) {
|
|
22
|
+
classes.push(`.item_level_${i}.active`);
|
|
23
|
+
}
|
|
24
|
+
if (classes.length > 0) $(classes.join(",")).removeClass("active");
|
|
25
|
+
$(`#${itemId}`).addClass(() => "active");
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const removeAllActiveClasses = () => {
|
|
29
|
+
$(".dropdown-item.active").removeClass("active");
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const hasSubLevels = (relation) =>
|
|
33
|
+
relation.fkeys?.length > 0 || relation.inboundKeys?.length > 0;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
*
|
|
37
|
+
* @param {*} param0
|
|
38
|
+
* @returns
|
|
39
|
+
*/
|
|
40
|
+
const Relation = ({ cfg }) => {
|
|
41
|
+
const { relation, ix, level, type, update, maxLevel, setMaxLevel } = cfg;
|
|
42
|
+
|
|
43
|
+
const setRelation = (key) => {
|
|
44
|
+
update(key.relPath);
|
|
45
|
+
removeAllActiveClasses();
|
|
46
|
+
toggleLayers(0, maxLevel);
|
|
47
|
+
setMaxLevel(maxLevelDefault, maxLevel);
|
|
48
|
+
};
|
|
49
|
+
const identifier = removeWhitespaces(
|
|
50
|
+
`${relation.name}_${relation.table}_${type}_${ix}_${level}_${rand_ident()}`
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
if (hasSubLevels(relation)) {
|
|
54
|
+
const itemId = `${identifier}_sub_key`;
|
|
55
|
+
const toggleId = `${identifier}_toggle`;
|
|
56
|
+
const nextDropId = `${identifier}_next_drop`;
|
|
57
|
+
return (
|
|
58
|
+
<div key={`${identifier}_key_div`}>
|
|
59
|
+
<li
|
|
60
|
+
id={itemId}
|
|
61
|
+
key={itemId}
|
|
62
|
+
className={`dropdown-item item_level_${level} ${
|
|
63
|
+
level < 5 ? "dropstart" : "dropdown"
|
|
64
|
+
} `}
|
|
65
|
+
role="button"
|
|
66
|
+
>
|
|
67
|
+
<div
|
|
68
|
+
key={toggleId}
|
|
69
|
+
id={toggleId}
|
|
70
|
+
className={`dropdown-toggle dropdown_level_${level}`}
|
|
71
|
+
role="button"
|
|
72
|
+
aria-expanded="false"
|
|
73
|
+
onClick={() => {
|
|
74
|
+
const layerCfg = {
|
|
75
|
+
layer: relation,
|
|
76
|
+
level: level + 1,
|
|
77
|
+
update,
|
|
78
|
+
maxLevel,
|
|
79
|
+
setMaxLevel,
|
|
80
|
+
};
|
|
81
|
+
ReactDOM.render(
|
|
82
|
+
<RelationLayer cfg={layerCfg} />,
|
|
83
|
+
document.getElementById(nextDropId),
|
|
84
|
+
() => {
|
|
85
|
+
toggleLayers(level, maxLevel, [`#${toggleId}`]);
|
|
86
|
+
setActiveClasses(level, maxLevel, itemId);
|
|
87
|
+
if (level > maxLevel) setMaxLevel(level);
|
|
88
|
+
}
|
|
89
|
+
);
|
|
90
|
+
}}
|
|
91
|
+
>
|
|
92
|
+
{keyLabel(relation, type)}
|
|
93
|
+
</div>
|
|
94
|
+
<div key={nextDropId} id={nextDropId} className="dropdown-menu"></div>
|
|
95
|
+
</li>
|
|
96
|
+
{/* has the layer a direct link ? */}
|
|
97
|
+
{relation.relPath ? (
|
|
98
|
+
<li
|
|
99
|
+
key={`${identifier}_direct_key`}
|
|
100
|
+
className="dropdown-item"
|
|
101
|
+
role="button"
|
|
102
|
+
onClick={() => {
|
|
103
|
+
setRelation(relation);
|
|
104
|
+
}}
|
|
105
|
+
>
|
|
106
|
+
{keyLabel(relation, type)}
|
|
107
|
+
</li>
|
|
108
|
+
) : (
|
|
109
|
+
""
|
|
110
|
+
)}
|
|
111
|
+
</div>
|
|
112
|
+
);
|
|
113
|
+
} else {
|
|
114
|
+
return (
|
|
115
|
+
<li
|
|
116
|
+
key={`${identifier}_key`}
|
|
117
|
+
className="dropdown-item"
|
|
118
|
+
role="button"
|
|
119
|
+
onClick={() => {
|
|
120
|
+
setRelation(relation);
|
|
121
|
+
}}
|
|
122
|
+
>
|
|
123
|
+
{keyLabel(relation, type)}
|
|
124
|
+
</li>
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
*
|
|
131
|
+
* @param {*} param0
|
|
132
|
+
* @returns
|
|
133
|
+
*/
|
|
134
|
+
const RelationLayer = ({ cfg }) => {
|
|
135
|
+
const { layer, level, update, maxLevel, setMaxLevel } = cfg;
|
|
136
|
+
const reactKey = (relation, type, ix) =>
|
|
137
|
+
`_rel_layer_${level}_${type}_${ix}_${relation.name}_`;
|
|
138
|
+
return (
|
|
139
|
+
<div>
|
|
140
|
+
<h5 className="join-table-header text-center">{layer.table}</h5>
|
|
141
|
+
<ul className="ps-0 mb-0">
|
|
142
|
+
{layer.fkeys.map((relation, ix) => (
|
|
143
|
+
<Relation
|
|
144
|
+
key={reactKey(relation, "fk", ix)}
|
|
145
|
+
cfg={{
|
|
146
|
+
relation,
|
|
147
|
+
ix,
|
|
148
|
+
level,
|
|
149
|
+
type: "fk",
|
|
150
|
+
update,
|
|
151
|
+
maxLevel,
|
|
152
|
+
setMaxLevel,
|
|
153
|
+
}}
|
|
154
|
+
/>
|
|
155
|
+
))}
|
|
156
|
+
{layer.inboundKeys.map((relation, ix) => (
|
|
157
|
+
<Relation
|
|
158
|
+
key={reactKey(relation, "inbound", ix)}
|
|
159
|
+
cfg={{
|
|
160
|
+
relation,
|
|
161
|
+
ix,
|
|
162
|
+
level,
|
|
163
|
+
type: "inbound",
|
|
164
|
+
update,
|
|
165
|
+
maxLevel,
|
|
166
|
+
setMaxLevel,
|
|
167
|
+
}}
|
|
168
|
+
/>
|
|
169
|
+
))}
|
|
170
|
+
</ul>
|
|
171
|
+
</div>
|
|
172
|
+
);
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
*
|
|
177
|
+
* @param {*} param0
|
|
178
|
+
* @returns
|
|
179
|
+
*/
|
|
180
|
+
export const RelationOnDemandPicker = ({ relations, update }) => {
|
|
181
|
+
const [maxLevel, setMaxLevel] = React.useState(maxLevelDefault);
|
|
182
|
+
const toggleId = "_relation_picker_toggle_";
|
|
183
|
+
const layerCfg = {
|
|
184
|
+
layer: relations,
|
|
185
|
+
level: 1,
|
|
186
|
+
update,
|
|
187
|
+
maxLevel,
|
|
188
|
+
setMaxLevel,
|
|
189
|
+
};
|
|
190
|
+
return (
|
|
191
|
+
<div>
|
|
192
|
+
<label>Relation</label>
|
|
193
|
+
<div style={{ zIndex: 10000 }} className="dropstart">
|
|
194
|
+
<button
|
|
195
|
+
id={toggleId}
|
|
196
|
+
className="btn btn-outline-primary dropdown-toggle dropdown_level_0 mb-1"
|
|
197
|
+
aria-expanded="false"
|
|
198
|
+
onClick={() => {
|
|
199
|
+
removeAllActiveClasses();
|
|
200
|
+
toggleLayers(0, maxLevel, [`#${toggleId}`]);
|
|
201
|
+
setMaxLevel(maxLevelDefault);
|
|
202
|
+
}}
|
|
203
|
+
>
|
|
204
|
+
Select
|
|
205
|
+
</button>
|
|
206
|
+
<div className="dropdown-menu">
|
|
207
|
+
<RelationLayer cfg={layerCfg} />
|
|
208
|
+
</div>
|
|
209
|
+
</div>
|
|
210
|
+
</div>
|
|
211
|
+
);
|
|
212
|
+
};
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* @subcategory components / elements
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import React, { Fragment,
|
|
7
|
+
import React, { Fragment, useEffect, useMemo } from "react";
|
|
8
8
|
import { useNode } from "@craftjs/core";
|
|
9
9
|
import optionsCtx from "../context";
|
|
10
10
|
import previewCtx from "../preview_context";
|
|
@@ -13,13 +13,13 @@ import {
|
|
|
13
13
|
fetchViewPreview,
|
|
14
14
|
ConfigForm,
|
|
15
15
|
setAPropGen,
|
|
16
|
-
FormulaTooltip,
|
|
17
16
|
buildOptions,
|
|
18
17
|
HelpTopicLink,
|
|
18
|
+
prepCacheAndFinder,
|
|
19
19
|
} from "./utils";
|
|
20
20
|
|
|
21
|
-
import { RelationPicker } from "./RelationPicker";
|
|
22
21
|
import { RelationBadges } from "./RelationBadges";
|
|
22
|
+
import { RelationOnDemandPicker } from "./RelationOnDemandPicker";
|
|
23
23
|
|
|
24
24
|
export /**
|
|
25
25
|
* @param {object} props
|
|
@@ -37,12 +37,18 @@ const View = ({ name, view, configuration, state }) => {
|
|
|
37
37
|
node_id,
|
|
38
38
|
connectors: { connect, drag },
|
|
39
39
|
} = useNode((node) => ({ selected: node.events.selected, node_id: node.id }));
|
|
40
|
-
const options = useContext(optionsCtx);
|
|
40
|
+
const options = React.useContext(optionsCtx);
|
|
41
41
|
|
|
42
|
-
|
|
43
|
-
|
|
42
|
+
let viewname = view;
|
|
43
|
+
if (viewname && viewname.includes(":")) {
|
|
44
|
+
const [prefix, rest] = viewname.split(":");
|
|
45
|
+
if (rest.startsWith(".")) viewname = prefix;
|
|
46
|
+
else viewname = rest;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const theview = options.views.find((v) => v.name === viewname);
|
|
44
50
|
const label = theview ? theview.label : view;
|
|
45
|
-
const { previews, setPreviews } = useContext(previewCtx);
|
|
51
|
+
const { previews, setPreviews } = React.useContext(previewCtx);
|
|
46
52
|
const myPreview = previews[node_id];
|
|
47
53
|
useEffect(() => {
|
|
48
54
|
fetchViewPreview({
|
|
@@ -73,14 +79,13 @@ const View = ({ name, view, configuration, state }) => {
|
|
|
73
79
|
};
|
|
74
80
|
|
|
75
81
|
export /**
|
|
76
|
-
* @returns
|
|
82
|
+
* @returns
|
|
77
83
|
* @category saltcorn-builder
|
|
78
84
|
* @subcategory components
|
|
79
85
|
* @namespace
|
|
80
86
|
*/
|
|
81
87
|
const ViewSettings = () => {
|
|
82
88
|
const node = useNode((node) => ({
|
|
83
|
-
view_name: node.data.props.view_name,
|
|
84
89
|
name: node.data.props.name,
|
|
85
90
|
view: node.data.props.view,
|
|
86
91
|
relation: node.data.props.relation,
|
|
@@ -99,13 +104,15 @@ const ViewSettings = () => {
|
|
|
99
104
|
node_id,
|
|
100
105
|
configuration,
|
|
101
106
|
extra_state_fml,
|
|
102
|
-
view_name,
|
|
103
107
|
} = node;
|
|
104
|
-
const options = useContext(optionsCtx);
|
|
105
|
-
const
|
|
108
|
+
const options = React.useContext(optionsCtx);
|
|
109
|
+
const { caches, finder } = useMemo(
|
|
110
|
+
() => prepCacheAndFinder(options),
|
|
111
|
+
[undefined]
|
|
112
|
+
);
|
|
106
113
|
const fixed_state_fields =
|
|
107
114
|
options.fixed_state_fields && options.fixed_state_fields[view];
|
|
108
|
-
const { setPreviews } = useContext(previewCtx);
|
|
115
|
+
const { setPreviews } = React.useContext(previewCtx);
|
|
109
116
|
|
|
110
117
|
const setAProp = setAPropGen(setProp);
|
|
111
118
|
let errorString = false;
|
|
@@ -115,33 +122,56 @@ const ViewSettings = () => {
|
|
|
115
122
|
errorString = error.message;
|
|
116
123
|
}
|
|
117
124
|
|
|
118
|
-
let viewname =
|
|
125
|
+
let viewname = view;
|
|
126
|
+
let hasLegacyRelation = false;
|
|
119
127
|
if (viewname && viewname.includes(":")) {
|
|
128
|
+
hasLegacyRelation = true;
|
|
120
129
|
const [prefix, rest] = viewname.split(":");
|
|
121
130
|
if (rest.startsWith(".")) viewname = prefix;
|
|
122
131
|
else viewname = rest;
|
|
123
132
|
}
|
|
124
133
|
if (viewname.includes(".")) viewname = viewname.split(".")[0];
|
|
134
|
+
const [relations, setRelations] = finder
|
|
135
|
+
? React.useState(
|
|
136
|
+
finder.findRelations(
|
|
137
|
+
options.tableName,
|
|
138
|
+
viewname,
|
|
139
|
+
options.excluded_subview_templates
|
|
140
|
+
)
|
|
141
|
+
)
|
|
142
|
+
: [undefined, undefined];
|
|
143
|
+
let safeRelation = relation;
|
|
144
|
+
if (!safeRelation && !hasLegacyRelation && relations?.paths.length > 0) {
|
|
145
|
+
safeRelation = relations.paths[0];
|
|
146
|
+
setProp((prop) => {
|
|
147
|
+
prop.relation = safeRelation;
|
|
148
|
+
});
|
|
149
|
+
}
|
|
125
150
|
const helpContext = { view_name: viewname };
|
|
126
151
|
if (options.tableName) helpContext.srcTable = options.tableName;
|
|
127
152
|
const set_view_name = (e) => {
|
|
128
153
|
if (e.target) {
|
|
129
154
|
const target_value = e.target.value;
|
|
130
|
-
setProp((prop) => (prop.view_name = target_value));
|
|
131
155
|
if (target_value !== viewname) {
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
156
|
+
const newRelations = finder.findRelations(
|
|
157
|
+
options.tableName,
|
|
158
|
+
target_value,
|
|
159
|
+
options.excluded_subview_templates
|
|
160
|
+
);
|
|
161
|
+
if (newRelations.paths.length > 0) {
|
|
162
|
+
setProp((prop) => {
|
|
163
|
+
prop.view = target_value;
|
|
164
|
+
prop.relation = newRelations.paths[0];
|
|
165
|
+
});
|
|
166
|
+
setRelations(newRelations);
|
|
167
|
+
}
|
|
138
168
|
}
|
|
139
169
|
}
|
|
140
170
|
};
|
|
141
171
|
|
|
142
172
|
return (
|
|
143
173
|
<div>
|
|
144
|
-
{
|
|
174
|
+
{relations ? (
|
|
145
175
|
<Fragment>
|
|
146
176
|
<div>
|
|
147
177
|
<label>View to {options.mode === "show" ? "embed" : "show"}</label>
|
|
@@ -151,36 +181,39 @@ const ViewSettings = () => {
|
|
|
151
181
|
onChange={set_view_name}
|
|
152
182
|
onBlur={set_view_name}
|
|
153
183
|
>
|
|
154
|
-
{options.
|
|
155
|
-
<option key={ix} value={
|
|
156
|
-
{
|
|
184
|
+
{options.views.map((v, ix) => (
|
|
185
|
+
<option key={ix} value={v.name}>
|
|
186
|
+
{v.label}
|
|
157
187
|
</option>
|
|
158
188
|
))}
|
|
159
189
|
</select>
|
|
160
190
|
</div>
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
191
|
+
{
|
|
192
|
+
<div>
|
|
193
|
+
<RelationOnDemandPicker
|
|
194
|
+
relations={relations.layers}
|
|
195
|
+
update={(relPath) => {
|
|
196
|
+
if (relPath.startsWith(".")) {
|
|
197
|
+
setProp((prop) => {
|
|
198
|
+
prop.view = viewname;
|
|
199
|
+
prop.relation = relPath;
|
|
200
|
+
});
|
|
201
|
+
} else {
|
|
202
|
+
setProp((prop) => {
|
|
203
|
+
prop.view = relPath;
|
|
204
|
+
prop.relation = undefined;
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
}}
|
|
208
|
+
/>
|
|
209
|
+
<RelationBadges
|
|
210
|
+
view={view}
|
|
211
|
+
relation={safeRelation}
|
|
212
|
+
parentTbl={options.tableName}
|
|
213
|
+
tableNameCache={caches.tableNameCache}
|
|
214
|
+
/>
|
|
215
|
+
</div>
|
|
216
|
+
}
|
|
184
217
|
</Fragment>
|
|
185
218
|
) : (
|
|
186
219
|
<div>
|
|
@@ -191,7 +224,7 @@ const ViewSettings = () => {
|
|
|
191
224
|
onChange={setAProp("view")}
|
|
192
225
|
onBlur={setAProp("view")}
|
|
193
226
|
>
|
|
194
|
-
{views.map((f, ix) => (
|
|
227
|
+
{options.views.map((f, ix) => (
|
|
195
228
|
<option key={ix} value={f.name}>
|
|
196
229
|
{f.label || f.name}
|
|
197
230
|
</option>
|