@saltcorn/markup 0.5.6-beta.3 → 0.6.0-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/builder.js +8 -0
- package/form.js +30 -14
- package/form.test.js +5 -5
- package/helpers.js +2 -2
- package/index.js +4 -3
- package/layout.js +37 -17
- package/markup.test.js +11 -0
- package/package.json +1 -1
- package/tags.js +9 -2
package/builder.js
CHANGED
|
@@ -41,6 +41,14 @@ module.exports = (
|
|
|
41
41
|
? `/static_assets/${version_tag}/fonticonpicker.react.css`
|
|
42
42
|
: "/fonticonpicker.react.css",
|
|
43
43
|
}),
|
|
44
|
+
link({
|
|
45
|
+
rel: "stylesheet",
|
|
46
|
+
type: "text/css",
|
|
47
|
+
media: "screen",
|
|
48
|
+
href: version_tag
|
|
49
|
+
? `/static_assets/${version_tag}/saltcorn-builder.css`
|
|
50
|
+
: "/saltcorn-builder.css",
|
|
51
|
+
}),
|
|
44
52
|
div({ id: "saltcorn-builder" }),
|
|
45
53
|
form(
|
|
46
54
|
{ action, method: "post", id: "scbuildform" },
|
package/form.js
CHANGED
|
@@ -43,13 +43,17 @@ const formRowWrap = (hdr, inner, error = "", fStyle, labelCols) =>
|
|
|
43
43
|
}),
|
|
44
44
|
},
|
|
45
45
|
hdr.input_type === "section_header"
|
|
46
|
-
? div(
|
|
46
|
+
? div(
|
|
47
|
+
{ class: `col-sm-12` },
|
|
48
|
+
h5(text(hdr.label)),
|
|
49
|
+
hdr.sublabel && p(i(hdr.sublabel))
|
|
50
|
+
)
|
|
47
51
|
: [
|
|
48
52
|
div(
|
|
49
53
|
{ class: isHoriz(fStyle) && `col-sm-${labelCols}` },
|
|
50
54
|
label(
|
|
51
55
|
{
|
|
52
|
-
for: `input${text_attr(hdr.form_name)}`,
|
|
56
|
+
for: `input${text_attr(hdr.form_name)}`,
|
|
53
57
|
},
|
|
54
58
|
text(hdr.label)
|
|
55
59
|
)
|
|
@@ -100,7 +104,7 @@ const innerField = (v, errors, nameAdd = "") => (hdr) => {
|
|
|
100
104
|
}"${maybe_disabled} data-fieldname="${text_attr(
|
|
101
105
|
hdr.form_name
|
|
102
106
|
)}" name="${text_attr(name)}" id="input${text_attr(name)}">${text(
|
|
103
|
-
v[hdr.form_name]
|
|
107
|
+
v[hdr.form_name] || ""
|
|
104
108
|
)}</textarea>`;
|
|
105
109
|
case "code":
|
|
106
110
|
return `<textarea mode="${
|
|
@@ -109,9 +113,9 @@ const innerField = (v, errors, nameAdd = "") => (hdr) => {
|
|
|
109
113
|
hdr.class || ""
|
|
110
114
|
}"${maybe_disabled} data-fieldname="${text_attr(
|
|
111
115
|
hdr.form_name
|
|
112
|
-
)}" name="${text_attr(name)}" id="input${text_attr(name)}">${
|
|
113
|
-
v[hdr.form_name]
|
|
114
|
-
|
|
116
|
+
)}" name="${text_attr(name)}" id="input${text_attr(name)}">${
|
|
117
|
+
v[hdr.form_name] || ""
|
|
118
|
+
}</textarea>`;
|
|
115
119
|
case "file":
|
|
116
120
|
if (hdr.attributes && hdr.attributes.select_file_where) {
|
|
117
121
|
hdr.input_type = "select";
|
|
@@ -137,9 +141,9 @@ const innerField = (v, errors, nameAdd = "") => (hdr) => {
|
|
|
137
141
|
hdr.class || ""
|
|
138
142
|
}"${maybe_disabled} data-fieldname="${text_attr(
|
|
139
143
|
hdr.form_name
|
|
140
|
-
)}" name="${name}" id="input${text_attr(name)}"
|
|
144
|
+
)}" name="${name}" id="input${text_attr(name)}"${
|
|
141
145
|
v && isdef(v[hdr.form_name])
|
|
142
|
-
? `value="${text_attr(v[hdr.form_name])}"`
|
|
146
|
+
? ` value="${text_attr(v[hdr.form_name])}"`
|
|
143
147
|
: ""
|
|
144
148
|
}>`;
|
|
145
149
|
const inner = hdr.postText
|
|
@@ -294,7 +298,16 @@ const renderFormLayout = (form) => {
|
|
|
294
298
|
action_size,
|
|
295
299
|
action_icon,
|
|
296
300
|
configuration,
|
|
301
|
+
action_bgcol,
|
|
302
|
+
action_bordercol,
|
|
303
|
+
action_textcol,
|
|
297
304
|
}) {
|
|
305
|
+
let style =
|
|
306
|
+
action_style === "btn-custom-color"
|
|
307
|
+
? `background-color: ${action_bgcol || "#000000"};border-color: ${
|
|
308
|
+
action_bordercol || "#000000"
|
|
309
|
+
}; color: ${action_textcol || "#000000"}`
|
|
310
|
+
: null;
|
|
298
311
|
if (action_name && action_name.startsWith("Login with ")) {
|
|
299
312
|
const method_label = action_name.replace("Login with ", "");
|
|
300
313
|
|
|
@@ -306,6 +319,7 @@ const renderFormLayout = (form) => {
|
|
|
306
319
|
action_style !== "btn-link" &&
|
|
307
320
|
`btn ${action_style || "btn-primary"} ${action_size || ""}`,
|
|
308
321
|
],
|
|
322
|
+
style,
|
|
309
323
|
},
|
|
310
324
|
action_icon ? i({ class: action_icon }) + " " : false,
|
|
311
325
|
action_label || action_name
|
|
@@ -316,7 +330,9 @@ const renderFormLayout = (form) => {
|
|
|
316
330
|
action_style === "btn-link"
|
|
317
331
|
? ""
|
|
318
332
|
: `btn ${action_style || "btn-primary"} ${action_size || ""}`
|
|
319
|
-
}"
|
|
333
|
+
}"${style ? ` style="${style}"` : ""}>${
|
|
334
|
+
action_icon ? `<i class="${action_icon}"></i> ` : ""
|
|
335
|
+
}${text(
|
|
320
336
|
action_label || form.submitLabel || action_name || "Save"
|
|
321
337
|
)}</button>`;
|
|
322
338
|
|
|
@@ -383,11 +399,11 @@ const renderForm = (form, csrfToken0) => {
|
|
|
383
399
|
const mkFormWithLayout = (form, csrfToken) => {
|
|
384
400
|
const hasFile = form.fields.some((f) => f.input_type === "file");
|
|
385
401
|
const csrfField = `<input type="hidden" name="_csrf" value="${csrfToken}">`;
|
|
386
|
-
const top = `<form action="${form.action}"
|
|
402
|
+
const top = `<form action="${form.action}"${
|
|
387
403
|
form.onChange ? ` onchange="${form.onChange}"` : ""
|
|
388
|
-
}class="form-namespace ${form.class || ""}" method="${
|
|
404
|
+
} class="form-namespace ${form.class || ""}" method="${
|
|
389
405
|
form.methodGET ? "get" : "post"
|
|
390
|
-
}"
|
|
406
|
+
}"${hasFile ? ' encType="multipart/form-data"' : ""}>`;
|
|
391
407
|
const blurbp = form.blurb
|
|
392
408
|
? Array.isArray(form.blurb)
|
|
393
409
|
? form.blurb.join("")
|
|
@@ -436,8 +452,8 @@ const mkForm = (form, csrfToken, errors = {}) => {
|
|
|
436
452
|
form.onChange ? ` onchange="${form.onChange}"` : ""
|
|
437
453
|
}class="form-namespace ${form.isStateForm ? "stateForm" : ""} ${
|
|
438
454
|
form.class || ""
|
|
439
|
-
}" method="${form.methodGET ? "get" : "post"}"
|
|
440
|
-
hasFile ? 'encType="multipart/form-data"' : ""
|
|
455
|
+
}" method="${form.methodGET ? "get" : "post"}"${
|
|
456
|
+
hasFile ? ' encType="multipart/form-data"' : ""
|
|
441
457
|
}>`;
|
|
442
458
|
//console.log(form.fields);
|
|
443
459
|
const flds = form.fields
|
package/form.test.js
CHANGED
|
@@ -24,10 +24,10 @@ describe("form render", () => {
|
|
|
24
24
|
},
|
|
25
25
|
],
|
|
26
26
|
});
|
|
27
|
-
const want = `<form action="/" class="form-namespace " method="post"
|
|
27
|
+
const want = `<form action="/" class="form-namespace " method="post">
|
|
28
28
|
<input type="hidden" name="_csrf" value=""><div class="form-group">
|
|
29
29
|
<div><label for="inputname">Name</label></div>
|
|
30
|
-
<div><input type="text" class="form-control " data-fieldname="name" name="name" id="inputname"
|
|
30
|
+
<div><input type="text" class="form-control " data-fieldname="name" name="name" id="inputname">
|
|
31
31
|
</div></div><div class="form-group row">
|
|
32
32
|
<div class="col-sm-12">
|
|
33
33
|
<button type="submit" class="btn btn-primary">Save</button>
|
|
@@ -62,10 +62,10 @@ describe("form render", () => {
|
|
|
62
62
|
],
|
|
63
63
|
},
|
|
64
64
|
});
|
|
65
|
-
const want = `<form action="/" class="form-namespace " method="post"
|
|
65
|
+
const want = `<form action="/" class="form-namespace " method="post">
|
|
66
66
|
<input type="hidden" name="_csrf" value="">
|
|
67
67
|
<h2>
|
|
68
|
-
<input type="text" class="form-control " data-fieldname="name" name="name" id="inputname"
|
|
68
|
+
<input type="text" class="form-control " data-fieldname="name" name="name" id="inputname">
|
|
69
69
|
</h2><br /></form>`;
|
|
70
70
|
expect(nolines(renderForm(form, ""))).toBe(nolines(want));
|
|
71
71
|
});
|
|
@@ -83,7 +83,7 @@ describe("form render", () => {
|
|
|
83
83
|
},
|
|
84
84
|
],
|
|
85
85
|
});
|
|
86
|
-
const want = `<form action="/" class="form-namespace " method="post"
|
|
86
|
+
const want = `<form action="/" class="form-namespace " method="post">
|
|
87
87
|
<input type="hidden" name="_csrf" value=""><div class="form-group">
|
|
88
88
|
<div><label for="inputname">Name</label></div>
|
|
89
89
|
<div><input type="text" class="form-control is-invalid " data-fieldname="name" name="name" id="inputname" value="Bar"><div>Not a foo</div>
|
package/helpers.js
CHANGED
|
@@ -31,8 +31,8 @@ const select_options = (v, hdr, force_required, neutral_label = "") => {
|
|
|
31
31
|
.map((o) => {
|
|
32
32
|
const label = typeof o === "string" ? o : o.label;
|
|
33
33
|
const value = typeof o === "string" ? o : o.value;
|
|
34
|
-
return `<option value="${text_attr(value)}"
|
|
35
|
-
isSelected(value) ? "selected" : ""
|
|
34
|
+
return `<option value="${text_attr(value)}"${
|
|
35
|
+
isSelected(value) ? " selected" : ""
|
|
36
36
|
}>${text(label)}</option>`;
|
|
37
37
|
})
|
|
38
38
|
.join("");
|
package/index.js
CHANGED
|
@@ -14,6 +14,7 @@ const post_btn = (
|
|
|
14
14
|
btnClass = "btn-primary",
|
|
15
15
|
onClick,
|
|
16
16
|
small,
|
|
17
|
+
style,
|
|
17
18
|
ajax,
|
|
18
19
|
reload_on_done,
|
|
19
20
|
reload_delay,
|
|
@@ -43,9 +44,9 @@ const post_btn = (
|
|
|
43
44
|
: confirm
|
|
44
45
|
? `onclick="return confirm('${req.__("Are you sure?")}')"`
|
|
45
46
|
: ""
|
|
46
|
-
} class="${klass} btn ${small ? "btn-sm" : ""} ${btnClass}"
|
|
47
|
-
|
|
48
|
-
}${s}</button></form>`;
|
|
47
|
+
} class="${klass} btn ${small ? "btn-sm" : ""} ${btnClass}"${
|
|
48
|
+
style ? ` style="${style}"` : ""
|
|
49
|
+
}>${icon ? `<i class="${icon}"></i> ` : ""}${s}</button></form>`;
|
|
49
50
|
/**
|
|
50
51
|
* UI Form for Delete Item confirmation
|
|
51
52
|
* @param href - href
|
package/layout.js
CHANGED
|
@@ -45,25 +45,26 @@ const makeSegments = (body, alerts) => {
|
|
|
45
45
|
return body;
|
|
46
46
|
} else return { above: [...alertsSegments, body] };
|
|
47
47
|
};
|
|
48
|
-
const applyTextStyle = (
|
|
49
|
-
|
|
48
|
+
const applyTextStyle = (segment, inner) => {
|
|
49
|
+
let style = segment.font ? { fontFamily: segment.font } : {};
|
|
50
|
+
switch (segment.textStyle) {
|
|
50
51
|
case "h1":
|
|
51
|
-
return h1(inner);
|
|
52
|
+
return h1(style, inner);
|
|
52
53
|
case "h2":
|
|
53
|
-
return h2(inner);
|
|
54
|
+
return h2(style, inner);
|
|
54
55
|
case "h3":
|
|
55
|
-
return h3(inner);
|
|
56
|
+
return h3(style, inner);
|
|
56
57
|
case "h4":
|
|
57
|
-
return h4(inner);
|
|
58
|
+
return h4(style, inner);
|
|
58
59
|
case "h5":
|
|
59
|
-
return h5(inner);
|
|
60
|
+
return h5(style, inner);
|
|
60
61
|
case "h6":
|
|
61
|
-
return h6(inner);
|
|
62
|
+
return h6(style, inner);
|
|
62
63
|
default:
|
|
63
|
-
return
|
|
64
|
-
? div({ class: textStyle || "" }, inner)
|
|
65
|
-
: textStyle
|
|
66
|
-
? span({ class: textStyle || "" }, inner)
|
|
64
|
+
return segment.block
|
|
65
|
+
? div({ class: segment.textStyle || "", style }, inner)
|
|
66
|
+
: segment.textStyle || segment.font
|
|
67
|
+
? span({ class: segment.textStyle || "", style }, inner)
|
|
67
68
|
: inner;
|
|
68
69
|
}
|
|
69
70
|
};
|
|
@@ -158,9 +159,9 @@ const render = ({ blockDispatch, layout, role, alerts, is_owner }) => {
|
|
|
158
159
|
? iconTag +
|
|
159
160
|
label(
|
|
160
161
|
{ for: `input${text(segment.labelFor)}` },
|
|
161
|
-
applyTextStyle(segment
|
|
162
|
+
applyTextStyle(segment, inner)
|
|
162
163
|
)
|
|
163
|
-
: iconTag + applyTextStyle(segment
|
|
164
|
+
: iconTag + applyTextStyle(segment, inner);
|
|
164
165
|
}
|
|
165
166
|
function go(segment, isTop, ix) {
|
|
166
167
|
if (!segment) return "";
|
|
@@ -214,6 +215,14 @@ const render = ({ blockDispatch, layout, role, alerts, is_owner }) => {
|
|
|
214
215
|
);
|
|
215
216
|
}
|
|
216
217
|
if (segment.type === "link") {
|
|
218
|
+
let style =
|
|
219
|
+
segment.link_style === "btn btn-custom-color"
|
|
220
|
+
? `background-color: ${
|
|
221
|
+
segment.link_bgcol || "#000000"
|
|
222
|
+
};border-color: ${segment.link_bordercol || "#000000"}; color: ${
|
|
223
|
+
segment.link_textcol || "#000000"
|
|
224
|
+
}`
|
|
225
|
+
: null;
|
|
217
226
|
return wrap(
|
|
218
227
|
segment,
|
|
219
228
|
isTop,
|
|
@@ -224,6 +233,7 @@ const render = ({ blockDispatch, layout, role, alerts, is_owner }) => {
|
|
|
224
233
|
class: [segment.link_style || "", segment.link_size || ""],
|
|
225
234
|
target: segment.target_blank ? "_blank" : false,
|
|
226
235
|
rel: segment.nofollow ? "nofollow" : false,
|
|
236
|
+
style,
|
|
227
237
|
},
|
|
228
238
|
segment.link_icon ? i({ class: segment.link_icon }) + " " : "",
|
|
229
239
|
segment.text
|
|
@@ -272,6 +282,7 @@ const render = ({ blockDispatch, layout, role, alerts, is_owner }) => {
|
|
|
272
282
|
vAlign,
|
|
273
283
|
hAlign,
|
|
274
284
|
block,
|
|
285
|
+
display,
|
|
275
286
|
imageSize,
|
|
276
287
|
borderWidth,
|
|
277
288
|
borderStyle,
|
|
@@ -296,6 +307,8 @@ const render = ({ blockDispatch, layout, role, alerts, is_owner }) => {
|
|
|
296
307
|
gradDirection,
|
|
297
308
|
fullPageWidth,
|
|
298
309
|
overflow,
|
|
310
|
+
rotate,
|
|
311
|
+
style,
|
|
299
312
|
} = segment;
|
|
300
313
|
if (hide) return "";
|
|
301
314
|
if (
|
|
@@ -312,7 +325,8 @@ const render = ({ blockDispatch, layout, role, alerts, is_owner }) => {
|
|
|
312
325
|
unit || segment[segKey + "Unit"] || "px"
|
|
313
326
|
};`;
|
|
314
327
|
const ppCustomCSS = (s) => (s ? s.split("\n").join("") + ";" : "");
|
|
315
|
-
const baseDisplayClass =
|
|
328
|
+
const baseDisplayClass =
|
|
329
|
+
block === false ? "inline-block" : display ? display : "block";
|
|
316
330
|
let displayClass = minScreenWidth
|
|
317
331
|
? `d-none d-${minScreenWidth}-${baseDisplayClass}`
|
|
318
332
|
: baseDisplayClass === "block"
|
|
@@ -325,6 +339,10 @@ const render = ({ blockDispatch, layout, role, alerts, is_owner }) => {
|
|
|
325
339
|
!segment[what] || allZero(segment[what])
|
|
326
340
|
? ""
|
|
327
341
|
: `${what}: ${segment[what].map((p) => p + "px").join(" ")};`;
|
|
342
|
+
let flexStyles = "";
|
|
343
|
+
Object.keys(style || {}).forEach((k) => {
|
|
344
|
+
flexStyles += `${k}:${style[k]};`;
|
|
345
|
+
});
|
|
328
346
|
return wrap(
|
|
329
347
|
segment,
|
|
330
348
|
isTop,
|
|
@@ -346,7 +364,7 @@ const render = ({ blockDispatch, layout, role, alerts, is_owner }) => {
|
|
|
346
364
|
],
|
|
347
365
|
onclick: segment.url ? `location.href='${segment.url}'` : false,
|
|
348
366
|
|
|
349
|
-
style: `${ppCustomCSS(customCSS || "")}${sizeProp(
|
|
367
|
+
style: `${flexStyles}${ppCustomCSS(customCSS || "")}${sizeProp(
|
|
350
368
|
"minHeight",
|
|
351
369
|
"min-height"
|
|
352
370
|
)}${sizeProp("height", "height")}${sizeProp(
|
|
@@ -378,7 +396,9 @@ const render = ({ blockDispatch, layout, role, alerts, is_owner }) => {
|
|
|
378
396
|
gradDirection || 0
|
|
379
397
|
}deg, ${gradStartColor}, ${gradEndColor});`
|
|
380
398
|
: ""
|
|
381
|
-
} ${setTextColor ? `color: ${textColor};` : ""}
|
|
399
|
+
} ${setTextColor ? `color: ${textColor};` : ""}${
|
|
400
|
+
rotate ? `transform: rotate(${rotate}deg);` : ""
|
|
401
|
+
}`,
|
|
382
402
|
...(showIfFormulaInputs
|
|
383
403
|
? {
|
|
384
404
|
"data-show-if": `showIfFormulaInputs(e, '${showIfFormulaInputs}')`,
|
package/markup.test.js
CHANGED
|
@@ -61,7 +61,18 @@ describe("tags", () => {
|
|
|
61
61
|
expect(div({ style: { color: "red", border: "1px solid black" } }, 5)).toBe(
|
|
62
62
|
'<div style="color:red;border:1px solid black">5</div>'
|
|
63
63
|
);
|
|
64
|
+
|
|
65
|
+
expect(
|
|
66
|
+
div({ style: { marginRight: "1px", border: "1px solid black" } }, 5)
|
|
67
|
+
).toBe('<div style="margin-right:1px;border:1px solid black">5</div>');
|
|
68
|
+
//border-top-left-radius
|
|
69
|
+
expect(
|
|
70
|
+
div({ style: { marginRight: "1px", borderTopLeftRadius: "3px" } }, 5)
|
|
71
|
+
).toBe('<div style="margin-right:1px;border-top-left-radius:3px">5</div>');
|
|
64
72
|
expect(hr({ style: { color: "red" } }, 5)).toBe('<hr style="color:red">');
|
|
73
|
+
expect(hr({ style: {} })).toBe("<hr>");
|
|
74
|
+
expect(hr({ style: null })).toBe("<hr>");
|
|
75
|
+
expect(div({ class: "foo", style: null })).toBe('<div class="foo"></div>');
|
|
65
76
|
});
|
|
66
77
|
|
|
67
78
|
it("escaping", () => {
|
package/package.json
CHANGED
package/tags.js
CHANGED
|
@@ -3,6 +3,10 @@ const escape = require("escape-html");
|
|
|
3
3
|
const htmlTags = require("html-tags");
|
|
4
4
|
const voidHtmlTags = new Set(require("html-tags/void"));
|
|
5
5
|
|
|
6
|
+
//https://stackoverflow.com/a/54246501
|
|
7
|
+
const camelToCssCase = (str) =>
|
|
8
|
+
str.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`);
|
|
9
|
+
|
|
6
10
|
const ppClasses = (cs) =>
|
|
7
11
|
typeof cs === "string" ? cs : !cs ? "" : cs.filter((c) => c).join(" ");
|
|
8
12
|
const ppClass = (c) => {
|
|
@@ -18,7 +22,7 @@ const ppStyles = (cs) =>
|
|
|
18
22
|
? cs.filter((c) => c).join(";")
|
|
19
23
|
: typeof cs === "object"
|
|
20
24
|
? Object.entries(cs)
|
|
21
|
-
.map(([k, v]) => `${k}:${v}`)
|
|
25
|
+
.map(([k, v]) => `${camelToCssCase(k)}:${v}`)
|
|
22
26
|
.join(";")
|
|
23
27
|
: "";
|
|
24
28
|
const ppStyle = (c) => {
|
|
@@ -50,7 +54,10 @@ const mkTag = (tnm, voidTag) => (...args) => {
|
|
|
50
54
|
if (Array.isArray(arg)) {
|
|
51
55
|
arg.forEach(argIter);
|
|
52
56
|
} else {
|
|
53
|
-
attribs += Object.entries(arg)
|
|
57
|
+
attribs += Object.entries(arg)
|
|
58
|
+
.map(ppAttrib)
|
|
59
|
+
.filter((s) => s)
|
|
60
|
+
.join(" ");
|
|
54
61
|
}
|
|
55
62
|
} else body += arg;
|
|
56
63
|
};
|