@saltcorn/builder 0.9.4-beta.2 → 0.9.4-beta.21
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 +23 -11
- package/package.json +3 -1
- package/src/components/Builder.js +92 -66
- package/src/components/Library.js +46 -22
- package/src/components/RenderNode.js +5 -0
- package/src/components/Toolbox.js +173 -165
- package/src/components/elements/Action.js +12 -0
- package/src/components/elements/Aggregation.js +193 -104
- package/src/components/elements/BoxModelEditor.js +8 -8
- package/src/components/elements/Column.js +16 -2
- package/src/components/elements/Columns.js +4 -4
- package/src/components/elements/Container.js +26 -2
- package/src/components/elements/DropMenu.js +31 -2
- package/src/components/elements/Field.js +22 -20
- package/src/components/elements/HTMLCode.js +1 -1
- package/src/components/elements/JoinField.js +25 -1
- package/src/components/elements/Link.js +1 -0
- package/src/components/elements/ListColumn.js +177 -0
- package/src/components/elements/ListColumns.js +62 -0
- package/src/components/elements/RelationBadges.js +53 -44
- package/src/components/elements/Tabs.js +118 -40
- package/src/components/elements/Text.js +4 -2
- package/src/components/elements/View.js +125 -68
- package/src/components/elements/ViewLink.js +100 -48
- package/src/components/elements/utils.js +198 -98
- package/src/components/storage.js +67 -5
- package/tests/relations_finder.test.js +58 -92
- package/tests/test_data.js +0 -163
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* @subcategory components / elements
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import React, { useContext } from "react";
|
|
7
|
+
import React, { useContext, useState, useEffect, Fragment } from "react";
|
|
8
8
|
import { useNode } from "@craftjs/core";
|
|
9
9
|
import optionsCtx from "../context";
|
|
10
10
|
import {
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
TextStyleRow,
|
|
14
14
|
setAPropGen,
|
|
15
15
|
buildOptions,
|
|
16
|
+
ConfigForm,
|
|
16
17
|
} from "./utils";
|
|
17
18
|
|
|
18
19
|
export /**
|
|
@@ -58,123 +59,209 @@ const AggregationSettings = () => {
|
|
|
58
59
|
aggwhere,
|
|
59
60
|
block,
|
|
60
61
|
textStyle,
|
|
62
|
+
agg_fieldview,
|
|
63
|
+
configuration,
|
|
61
64
|
} = useNode((node) => ({
|
|
62
65
|
agg_relation: node.data.props.agg_relation,
|
|
63
66
|
agg_field: node.data.props.agg_field,
|
|
64
67
|
aggwhere: node.data.props.aggwhere,
|
|
65
68
|
stat: node.data.props.stat,
|
|
66
69
|
block: node.data.props.block,
|
|
70
|
+
agg_fieldview: node.data.props.agg_fieldview,
|
|
71
|
+
configuration: node.data.props.configuration,
|
|
67
72
|
textStyle: node.data.props.textStyle,
|
|
68
73
|
}));
|
|
69
74
|
const options = useContext(optionsCtx);
|
|
70
75
|
const setAProp = setAPropGen(setProp);
|
|
71
76
|
|
|
77
|
+
const targetField = options.agg_field_opts[agg_relation]?.find?.(
|
|
78
|
+
(f) => f.name === agg_field
|
|
79
|
+
);
|
|
80
|
+
const targetFieldType = targetField?.ftype;
|
|
81
|
+
const outcomeType =
|
|
82
|
+
stat === "Count" || stat === "CountUnique"
|
|
83
|
+
? "Integer"
|
|
84
|
+
: stat === "Array_Agg"
|
|
85
|
+
? "Array"
|
|
86
|
+
: targetFieldType;
|
|
87
|
+
const fvs = options.agg_fieldview_options[outcomeType];
|
|
88
|
+
|
|
89
|
+
const [fetchedCfgFields, setFetchedCfgFields] = useState([]);
|
|
90
|
+
const cfgFields = fetchedCfgFields;
|
|
91
|
+
useEffect(() => {
|
|
92
|
+
fetch(
|
|
93
|
+
`/field/fieldviewcfgform/${
|
|
94
|
+
targetField?.table_name || options.tableName
|
|
95
|
+
}?accept=json`,
|
|
96
|
+
{
|
|
97
|
+
method: "POST",
|
|
98
|
+
headers: {
|
|
99
|
+
"Content-Type": "application/json",
|
|
100
|
+
"CSRF-Token": options.csrfToken,
|
|
101
|
+
"X-Requested-With": "XMLHttpRequest",
|
|
102
|
+
},
|
|
103
|
+
body: JSON.stringify({
|
|
104
|
+
agg_outcome_type: outcomeType,
|
|
105
|
+
agg_fieldview,
|
|
106
|
+
agg_field: targetField?.name,
|
|
107
|
+
}),
|
|
108
|
+
}
|
|
109
|
+
)
|
|
110
|
+
.then(function (response) {
|
|
111
|
+
if (response.status < 399) return response.json();
|
|
112
|
+
else return [];
|
|
113
|
+
})
|
|
114
|
+
.then(setFetchedCfgFields);
|
|
115
|
+
}, [outcomeType, agg_fieldview]);
|
|
116
|
+
|
|
72
117
|
return (
|
|
73
|
-
<
|
|
74
|
-
<
|
|
75
|
-
<
|
|
76
|
-
|
|
77
|
-
<
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
{f
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
>
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
<tr>
|
|
120
|
-
<td>
|
|
121
|
-
<label>Statistic</label>
|
|
122
|
-
</td>
|
|
123
|
-
<td>
|
|
124
|
-
<select
|
|
125
|
-
value={stat}
|
|
126
|
-
className="form-control form-select"
|
|
127
|
-
onChange={setAProp("stat")}
|
|
128
|
-
onBlur={setAProp("stat")}
|
|
129
|
-
>
|
|
130
|
-
{buildOptions(
|
|
131
|
-
[
|
|
132
|
-
"Count",
|
|
133
|
-
"CountUnique",
|
|
134
|
-
"Avg",
|
|
135
|
-
"Sum",
|
|
136
|
-
"Max",
|
|
137
|
-
"Min",
|
|
138
|
-
"Array_Agg",
|
|
139
|
-
],
|
|
140
|
-
{ valAttr: true }
|
|
141
|
-
)}
|
|
142
|
-
{options.fields
|
|
143
|
-
.filter((f) => f.type === "Date" || f.type.name === "Date")
|
|
144
|
-
.map((f) => (
|
|
145
|
-
<option value={`Latest ${f.name}`}>Latest {f.name}</option>
|
|
146
|
-
))}
|
|
147
|
-
{options.fields
|
|
148
|
-
.filter((f) => f.type === "Date" || f.type.name === "Date")
|
|
149
|
-
.map((f) => (
|
|
150
|
-
<option value={`Earliest ${f.name}`}>
|
|
151
|
-
Earliest {f.name}
|
|
118
|
+
<Fragment>
|
|
119
|
+
<table>
|
|
120
|
+
<tbody>
|
|
121
|
+
{options.mode === "filter" ? null : (
|
|
122
|
+
<tr>
|
|
123
|
+
<td>
|
|
124
|
+
<label>Relation</label>
|
|
125
|
+
</td>
|
|
126
|
+
<td>
|
|
127
|
+
<select
|
|
128
|
+
className="form-control form-select"
|
|
129
|
+
value={agg_relation}
|
|
130
|
+
onChange={(e) => {
|
|
131
|
+
if (!e.target) return;
|
|
132
|
+
const value = e.target.value;
|
|
133
|
+
setProp((prop) => {
|
|
134
|
+
prop.agg_relation = value;
|
|
135
|
+
const fs = options.agg_field_opts[value];
|
|
136
|
+
if (fs && fs.length > 0) prop.agg_field = fs[0]?.name;
|
|
137
|
+
});
|
|
138
|
+
}}
|
|
139
|
+
>
|
|
140
|
+
{options.child_field_list.map((f, ix) => (
|
|
141
|
+
<option key={ix} value={f}>
|
|
142
|
+
{f}
|
|
143
|
+
</option>
|
|
144
|
+
))}
|
|
145
|
+
</select>
|
|
146
|
+
</td>
|
|
147
|
+
</tr>
|
|
148
|
+
)}
|
|
149
|
+
<tr>
|
|
150
|
+
<td>
|
|
151
|
+
<label>
|
|
152
|
+
{options.mode === "filter" ? "Field" : "Child table field"}
|
|
153
|
+
</label>
|
|
154
|
+
</td>
|
|
155
|
+
<td>
|
|
156
|
+
<select
|
|
157
|
+
className="form-control form-select"
|
|
158
|
+
value={agg_field}
|
|
159
|
+
onChange={setAProp("agg_field")}
|
|
160
|
+
>
|
|
161
|
+
{(options.agg_field_opts[agg_relation] || []).map((f, ix) => (
|
|
162
|
+
<option key={ix} value={f.name}>
|
|
163
|
+
{f.label}
|
|
152
164
|
</option>
|
|
153
165
|
))}
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
166
|
+
</select>
|
|
167
|
+
</td>
|
|
168
|
+
</tr>
|
|
169
|
+
<tr>
|
|
170
|
+
<td>
|
|
171
|
+
<label>Statistic</label>
|
|
172
|
+
</td>
|
|
173
|
+
<td>
|
|
174
|
+
<select
|
|
175
|
+
value={stat}
|
|
176
|
+
className="form-control form-select"
|
|
177
|
+
onChange={setAProp("stat")}
|
|
178
|
+
onBlur={setAProp("stat")}
|
|
179
|
+
>
|
|
180
|
+
{buildOptions(
|
|
181
|
+
[
|
|
182
|
+
"Count",
|
|
183
|
+
"CountUnique",
|
|
184
|
+
"Avg",
|
|
185
|
+
"Sum",
|
|
186
|
+
"Max",
|
|
187
|
+
"Min",
|
|
188
|
+
"Array_Agg",
|
|
189
|
+
],
|
|
190
|
+
{ valAttr: true }
|
|
191
|
+
)}
|
|
192
|
+
{options.fields
|
|
193
|
+
.filter((f) => f.type === "Date" || f.type.name === "Date")
|
|
194
|
+
.map((f, ix) => (
|
|
195
|
+
<option key={ix} value={`Latest ${f.name}`}>
|
|
196
|
+
Latest {f.name}
|
|
197
|
+
</option>
|
|
198
|
+
))}
|
|
199
|
+
{options.fields
|
|
200
|
+
.filter((f) => f.type === "Date" || f.type.name === "Date")
|
|
201
|
+
.map((f, ix) => (
|
|
202
|
+
<option key={ix} value={`Earliest ${f.name}`}>
|
|
203
|
+
Earliest {f.name}
|
|
204
|
+
</option>
|
|
205
|
+
))}
|
|
206
|
+
</select>
|
|
207
|
+
</td>
|
|
208
|
+
</tr>
|
|
209
|
+
<tr>
|
|
210
|
+
<td>
|
|
211
|
+
<label>Where</label>
|
|
212
|
+
</td>
|
|
213
|
+
<td>
|
|
214
|
+
<input
|
|
215
|
+
type="text"
|
|
216
|
+
className="form-control"
|
|
217
|
+
value={aggwhere}
|
|
218
|
+
onChange={setAProp("aggwhere")}
|
|
219
|
+
/>
|
|
220
|
+
</td>
|
|
221
|
+
</tr>
|
|
222
|
+
{fvs && (
|
|
223
|
+
<tr>
|
|
224
|
+
<td>
|
|
225
|
+
<label>Field view</label>
|
|
226
|
+
</td>
|
|
227
|
+
|
|
228
|
+
<td>
|
|
229
|
+
<select
|
|
230
|
+
value={agg_fieldview}
|
|
231
|
+
className="form-control form-select"
|
|
232
|
+
onChange={(e) => {
|
|
233
|
+
if (!e.target) return;
|
|
234
|
+
const value = e.target.value;
|
|
235
|
+
setProp((prop) => (prop.agg_fieldview = value));
|
|
236
|
+
//refetchPreview({ fieldview: value });
|
|
237
|
+
}}
|
|
238
|
+
>
|
|
239
|
+
{(fvs || []).map((fvnm, ix) => (
|
|
240
|
+
<option key={ix} value={fvnm}>
|
|
241
|
+
{fvnm}
|
|
242
|
+
</option>
|
|
243
|
+
))}
|
|
244
|
+
</select>
|
|
245
|
+
</td>
|
|
246
|
+
</tr>
|
|
247
|
+
)}
|
|
248
|
+
|
|
249
|
+
<TextStyleRow textStyle={textStyle} setProp={setProp} />
|
|
250
|
+
<tr>
|
|
251
|
+
<td colSpan="2">
|
|
252
|
+
<BlockSetting block={block} setProp={setProp} />
|
|
253
|
+
</td>
|
|
254
|
+
</tr>
|
|
255
|
+
</tbody>
|
|
256
|
+
</table>{" "}
|
|
257
|
+
{cfgFields ? (
|
|
258
|
+
<ConfigForm
|
|
259
|
+
fields={cfgFields}
|
|
260
|
+
configuration={configuration || {}}
|
|
261
|
+
setProp={setProp}
|
|
262
|
+
/>
|
|
263
|
+
) : null}
|
|
264
|
+
</Fragment>
|
|
178
265
|
);
|
|
179
266
|
};
|
|
180
267
|
|
|
@@ -194,6 +281,8 @@ Aggregation.craft = {
|
|
|
194
281
|
"agg_field",
|
|
195
282
|
"aggwhere",
|
|
196
283
|
"stat",
|
|
284
|
+
"agg_fieldview",
|
|
285
|
+
{ name: "configuration", default: {} },
|
|
197
286
|
],
|
|
198
287
|
},
|
|
199
288
|
};
|
|
@@ -61,7 +61,7 @@ const BoxModelEditor = ({ setProp, node, sizeWithStyle }) => {
|
|
|
61
61
|
onClick={() => setCatAndDir("margin", "top")}
|
|
62
62
|
>
|
|
63
63
|
<input
|
|
64
|
-
|
|
64
|
+
readOnly={true}
|
|
65
65
|
type="text"
|
|
66
66
|
autoComplete="off"
|
|
67
67
|
name="boxmodel-ex-1_top_margin"
|
|
@@ -92,7 +92,7 @@ const BoxModelEditor = ({ setProp, node, sizeWithStyle }) => {
|
|
|
92
92
|
onClick={() => setCatAndDir("border", "top")}
|
|
93
93
|
>
|
|
94
94
|
<input
|
|
95
|
-
|
|
95
|
+
readOnly={true}
|
|
96
96
|
type="text"
|
|
97
97
|
autoComplete="off"
|
|
98
98
|
name="boxmodel-ex-1_top_border"
|
|
@@ -125,7 +125,7 @@ const BoxModelEditor = ({ setProp, node, sizeWithStyle }) => {
|
|
|
125
125
|
onClick={() => setCatAndDir("padding", "top")}
|
|
126
126
|
>
|
|
127
127
|
<input
|
|
128
|
-
|
|
128
|
+
readOnly={true}
|
|
129
129
|
type="text"
|
|
130
130
|
autoComplete="off"
|
|
131
131
|
name="boxmodel-ex-1_top_padding"
|
|
@@ -140,7 +140,7 @@ const BoxModelEditor = ({ setProp, node, sizeWithStyle }) => {
|
|
|
140
140
|
onClick={() => setSelectedCategory("size")}
|
|
141
141
|
>
|
|
142
142
|
<input
|
|
143
|
-
|
|
143
|
+
readOnly={true}
|
|
144
144
|
type="text"
|
|
145
145
|
autoComplete="off"
|
|
146
146
|
name="boxmodel-ex-1_width"
|
|
@@ -155,7 +155,7 @@ const BoxModelEditor = ({ setProp, node, sizeWithStyle }) => {
|
|
|
155
155
|
/>
|
|
156
156
|
x
|
|
157
157
|
<input
|
|
158
|
-
|
|
158
|
+
readOnly={true}
|
|
159
159
|
type="text"
|
|
160
160
|
autoComplete="off"
|
|
161
161
|
name="boxmodel-ex-1_height"
|
|
@@ -174,7 +174,7 @@ const BoxModelEditor = ({ setProp, node, sizeWithStyle }) => {
|
|
|
174
174
|
onClick={() => setCatAndDir("padding", "bottom")}
|
|
175
175
|
>
|
|
176
176
|
<input
|
|
177
|
-
|
|
177
|
+
readOnly={true}
|
|
178
178
|
type="text"
|
|
179
179
|
autoComplete="off"
|
|
180
180
|
name="boxmodel-ex-1_bottom_padding"
|
|
@@ -199,7 +199,7 @@ const BoxModelEditor = ({ setProp, node, sizeWithStyle }) => {
|
|
|
199
199
|
onClick={() => setCatAndDir("border", "bottom")}
|
|
200
200
|
>
|
|
201
201
|
<input
|
|
202
|
-
|
|
202
|
+
readOnly={true}
|
|
203
203
|
type="text"
|
|
204
204
|
autoComplete="off"
|
|
205
205
|
name="boxmodel-ex-1_bottom_border"
|
|
@@ -228,7 +228,7 @@ const BoxModelEditor = ({ setProp, node, sizeWithStyle }) => {
|
|
|
228
228
|
onClick={() => setCatAndDir("margin", "bottom")}
|
|
229
229
|
>
|
|
230
230
|
<input
|
|
231
|
-
|
|
231
|
+
readOnly={true}
|
|
232
232
|
type="text"
|
|
233
233
|
autoComplete="off"
|
|
234
234
|
name="boxmodel-ex-1_bottom_margin"
|
|
@@ -25,12 +25,20 @@ const Column = ({ children, align }) => {
|
|
|
25
25
|
id,
|
|
26
26
|
connectors: { connect, drag },
|
|
27
27
|
} = useNode((node) => ({ selected: node.events.selected }));
|
|
28
|
+
const options = useContext(optionsCtx);
|
|
29
|
+
|
|
28
30
|
return (
|
|
29
31
|
<div
|
|
30
|
-
className={selected ? "selected-node" : ""}
|
|
32
|
+
className={`${selected ? "selected-node" : ""} ${
|
|
33
|
+
options.mode === "list" ? "flex-50 list-col-contents" : ""
|
|
34
|
+
}`}
|
|
31
35
|
ref={(dom) => connect(drag(dom))}
|
|
32
36
|
>
|
|
33
|
-
<div
|
|
37
|
+
<div
|
|
38
|
+
className={`canvas ${id === "ROOT" ? "root-canvas" : ""} ${
|
|
39
|
+
options.mode === "list" ? "list-empty-msg list-col-canvas" : ""
|
|
40
|
+
}`}
|
|
41
|
+
>
|
|
34
42
|
{children}
|
|
35
43
|
</div>
|
|
36
44
|
</div>
|
|
@@ -56,6 +64,12 @@ Column.craft = {
|
|
|
56
64
|
props: {},
|
|
57
65
|
rules: {
|
|
58
66
|
canDrag: () => true,
|
|
67
|
+
canMoveIn: (incomming, current) => {
|
|
68
|
+
if (current?.data?.props?.singleOccupancy && current.data.nodes?.length)
|
|
69
|
+
return false;
|
|
70
|
+
|
|
71
|
+
return true;
|
|
72
|
+
},
|
|
59
73
|
},
|
|
60
74
|
related: {
|
|
61
75
|
settings: ColumnSettings,
|
|
@@ -250,7 +250,7 @@ const ColumnsSettings = () => {
|
|
|
250
250
|
setProp={setProp}
|
|
251
251
|
props={node}
|
|
252
252
|
></ConfigField>
|
|
253
|
-
<table>
|
|
253
|
+
<table className="w-100">
|
|
254
254
|
<tbody>
|
|
255
255
|
<SettingsSectionHeaderRow title="Align" />
|
|
256
256
|
<SettingsRow
|
|
@@ -259,9 +259,9 @@ const ColumnsSettings = () => {
|
|
|
259
259
|
label: "Vertical",
|
|
260
260
|
type: "btn_select",
|
|
261
261
|
options: [
|
|
262
|
-
{ value: "start", title: "
|
|
263
|
-
{ value: "center", title: "
|
|
264
|
-
{ value: "end", title: "
|
|
262
|
+
{ value: "start", title: "Start", label: <AlignTop /> },
|
|
263
|
+
{ value: "center", title: "Center", label: <AlignMiddle /> },
|
|
264
|
+
{ value: "end", title: "End", label: <AlignBottom /> },
|
|
265
265
|
],
|
|
266
266
|
}}
|
|
267
267
|
node={colSetsNode}
|
|
@@ -222,6 +222,7 @@ const ContainerSettings = () => {
|
|
|
222
222
|
display: node.data.props.display,
|
|
223
223
|
style: node.data.props.style,
|
|
224
224
|
imgResponsiveWidths: node.data.props.imgResponsiveWidths,
|
|
225
|
+
click_action: node.data.props.click_action,
|
|
225
226
|
}));
|
|
226
227
|
const {
|
|
227
228
|
actions: { setProp },
|
|
@@ -251,6 +252,7 @@ const ContainerSettings = () => {
|
|
|
251
252
|
overflow,
|
|
252
253
|
htmlElement,
|
|
253
254
|
imgResponsiveWidths,
|
|
255
|
+
click_action,
|
|
254
256
|
} = node;
|
|
255
257
|
const options = useContext(optionsCtx);
|
|
256
258
|
const { uploadedFiles } = useContext(previewCtx);
|
|
@@ -738,7 +740,7 @@ const ContainerSettings = () => {
|
|
|
738
740
|
)}
|
|
739
741
|
{["show", "edit", "filter"].includes(options.mode) && (
|
|
740
742
|
<tr>
|
|
741
|
-
<td>
|
|
743
|
+
<td colSpan={2}>
|
|
742
744
|
<input
|
|
743
745
|
type="text"
|
|
744
746
|
className="form-control text-to-display"
|
|
@@ -844,7 +846,29 @@ const ContainerSettings = () => {
|
|
|
844
846
|
onChange={setAProp("url")}
|
|
845
847
|
/>
|
|
846
848
|
</OrFormula>
|
|
847
|
-
|
|
849
|
+
{options.triggerActions ? (
|
|
850
|
+
<Fragment>
|
|
851
|
+
<label>Click action</label>
|
|
852
|
+
<select
|
|
853
|
+
value={click_action}
|
|
854
|
+
className="form-control form-select"
|
|
855
|
+
onChange={(e) => {
|
|
856
|
+
if (!e.target) return;
|
|
857
|
+
const value = e.target.value;
|
|
858
|
+
setProp((prop) => {
|
|
859
|
+
prop.click_action = value;
|
|
860
|
+
});
|
|
861
|
+
}}
|
|
862
|
+
>
|
|
863
|
+
<option value="">None</option>
|
|
864
|
+
{options.triggerActions.map((f, ix) => (
|
|
865
|
+
<option key={ix} value={f}>
|
|
866
|
+
{f}
|
|
867
|
+
</option>
|
|
868
|
+
))}
|
|
869
|
+
</select>
|
|
870
|
+
</Fragment>
|
|
871
|
+
) : null}
|
|
848
872
|
<label>Hover color</label>
|
|
849
873
|
<select
|
|
850
874
|
value={hoverColor}
|
|
@@ -8,7 +8,11 @@ import React, { Fragment, useState } from "react";
|
|
|
8
8
|
import { Element, useNode } from "@craftjs/core";
|
|
9
9
|
import { Column } from "./Column";
|
|
10
10
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
|
11
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
faCaretDown,
|
|
13
|
+
faCaretSquareLeft,
|
|
14
|
+
faCaretSquareRight,
|
|
15
|
+
} from "@fortawesome/free-solid-svg-icons";
|
|
12
16
|
import {
|
|
13
17
|
SettingsRow,
|
|
14
18
|
BlockSetting,
|
|
@@ -36,6 +40,7 @@ const DropMenu = ({
|
|
|
36
40
|
action_textcol,
|
|
37
41
|
block,
|
|
38
42
|
label,
|
|
43
|
+
menu_direction,
|
|
39
44
|
}) => {
|
|
40
45
|
const {
|
|
41
46
|
selected,
|
|
@@ -72,7 +77,7 @@ const DropMenu = ({
|
|
|
72
77
|
<div
|
|
73
78
|
className={`dropdown-menu dropmenu-dropdown ${
|
|
74
79
|
showDropdown ? "show" : ""
|
|
75
|
-
}`}
|
|
80
|
+
} ${menu_direction === "end" ? "dropdown-menu-end" : ""}`}
|
|
76
81
|
>
|
|
77
82
|
<div className="canvas d-flex flex-column">{children}</div>
|
|
78
83
|
</div>
|
|
@@ -96,6 +101,7 @@ const DropMenuSettings = () => {
|
|
|
96
101
|
action_bgcol: node.data.props.action_bgcol,
|
|
97
102
|
action_bordercol: node.data.props.action_bordercol,
|
|
98
103
|
action_textcol: node.data.props.action_textcol,
|
|
104
|
+
menu_direction: node.data.props.menu_direction,
|
|
99
105
|
}));
|
|
100
106
|
const {
|
|
101
107
|
actions: { setProp },
|
|
@@ -118,12 +124,34 @@ const DropMenuSettings = () => {
|
|
|
118
124
|
setProp={setProp}
|
|
119
125
|
keyPrefix="action_"
|
|
120
126
|
values={node}
|
|
127
|
+
allowRunOnLoad={false}
|
|
121
128
|
/>
|
|
122
129
|
<tr>
|
|
123
130
|
<td colSpan="2">
|
|
124
131
|
<BlockSetting block={block} setProp={setProp} />
|
|
125
132
|
</td>
|
|
126
133
|
</tr>
|
|
134
|
+
<SettingsRow
|
|
135
|
+
field={{
|
|
136
|
+
label: "Drop direction",
|
|
137
|
+
name: "menu_direction",
|
|
138
|
+
type: "btn_select",
|
|
139
|
+
options: [
|
|
140
|
+
{
|
|
141
|
+
value: "end",
|
|
142
|
+
title: "End",
|
|
143
|
+
label: <FontAwesomeIcon icon={faCaretSquareLeft} />,
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
value: "start",
|
|
147
|
+
title: "Start",
|
|
148
|
+
label: <FontAwesomeIcon icon={faCaretSquareRight} />,
|
|
149
|
+
},
|
|
150
|
+
],
|
|
151
|
+
}}
|
|
152
|
+
node={node}
|
|
153
|
+
setProp={setProp}
|
|
154
|
+
/>
|
|
127
155
|
</tbody>
|
|
128
156
|
</table>
|
|
129
157
|
);
|
|
@@ -151,6 +179,7 @@ DropMenu.craft = {
|
|
|
151
179
|
"action_bgcol",
|
|
152
180
|
"action_bordercol",
|
|
153
181
|
"action_textcol",
|
|
182
|
+
"menu_direction",
|
|
154
183
|
],
|
|
155
184
|
},
|
|
156
185
|
};
|
|
@@ -219,26 +219,28 @@ const FieldSettings = () => {
|
|
|
219
219
|
</td>
|
|
220
220
|
</tr>
|
|
221
221
|
)}
|
|
222
|
-
|
|
223
|
-
<
|
|
224
|
-
|
|
225
|
-
<
|
|
226
|
-
<
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
222
|
+
{options.mode === "show" || options.mode === "list" ? (
|
|
223
|
+
<tr>
|
|
224
|
+
<td></td>
|
|
225
|
+
<td>
|
|
226
|
+
<div className="form-check">
|
|
227
|
+
<input
|
|
228
|
+
className="form-check-input"
|
|
229
|
+
name="inline"
|
|
230
|
+
type="checkbox"
|
|
231
|
+
checked={click_to_edit}
|
|
232
|
+
onChange={(e) => {
|
|
233
|
+
if (e && e.target) {
|
|
234
|
+
const target_value = e.target.checked;
|
|
235
|
+
setProp((prop) => (prop.click_to_edit = target_value));
|
|
236
|
+
}
|
|
237
|
+
}}
|
|
238
|
+
/>
|
|
239
|
+
<label className="form-check-label">Click to edit?</label>
|
|
240
|
+
</div>
|
|
241
|
+
</td>
|
|
242
|
+
</tr>
|
|
243
|
+
) : null}
|
|
242
244
|
|
|
243
245
|
{!(blockDisplay && blockDisplay.includes(fieldview)) && (
|
|
244
246
|
<tr>
|