Skip to content

Commit

Permalink
Add checkbox element type
Browse files Browse the repository at this point in the history
  • Loading branch information
Rosuav committed Nov 1, 2024
1 parent 5ffcece commit d6b8cfa
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 11 deletions.
22 changes: 18 additions & 4 deletions httpstatic/chan_form.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {choc, set_content, DOM, on} from "https://rosuav.github.io/choc/factory.js";
const {BUTTON, DIV, INPUT, LABEL, LI, P, PRE, TD, TIME, TR} = choc; //autoimport
const {BUTTON, DIV, INPUT, LABEL, LI, P, PRE, TD, TIME, TR, UL} = choc; //autoimport
import {simpleconfirm} from "$$static||utils.js$$";

function format_time(ts) {
Expand All @@ -21,7 +21,7 @@ export const autorender = {
]));},
}

const render_element = {
const render_element = { //Matches _element_types (see Pike code)
"": el => P("Unknown element type - something went wrong - " + el.type),
//({"twitchid", "Twitch username"}), //If mandatory, will force user to be logged in to submit
simple: el => [ //extcall
Expand All @@ -33,10 +33,21 @@ const render_element = {
P("Paragraph input"),
LABEL(["Label: ", INPUT({name: "label", value: el.label || ""}), " - shown in the form"]),
],
//({"paragraph", "Paragraph input"}),
//({"address", "Street address"}),
//({"radio", "Selection (radio) buttons"}),
//({"checkbox", "Check box(es)"}),
checkbox: el => [ //extcall
P("Set of checkboxes"),
UL([
(el.label || []).map((l, i) =>
LI([
LABEL(["Label " + (i+1) + ": ", INPUT({name: "label[" + i + "]", value: l || ""})]),
" ",
BUTTON({type: "button", class: "deletefield", "data-field": "label[" + i + "]"}, "x"),
])
),
LI(LABEL(["Add label: ", INPUT({name: "label[" + (el.label || []).length + "]", value: ""})])),
]),
],
};

let editing = null;
Expand All @@ -47,6 +58,7 @@ function openform(f) {
if (el.type === "checkbox") el.checked = !!f[key];
else el.value = f[key] || "";
});
DOM("#viewform").href = "form?form=" + f.id;
DOM("#viewresp").href = "form?responses=" + f.id;
set_content("#formelements", (f.elements||[]).map((el, idx) => DIV({class: "element", "data-idx": idx}, [
DIV({class: "header"}, [
Expand Down Expand Up @@ -89,6 +101,8 @@ on("click", "#delete_form", simpleconfirm("Are you sure? This cannot be undone!"

on("change", ".element input", e => ws_sync.send({cmd: "edit_element", id: editing, idx: +e.match.closest_data("idx"), field: e.match.name, value: e.match.value}));

on("click", ".element .deletefield", e => ws_sync.send({cmd: "edit_element", id: editing, idx: +e.match.closest_data("idx"), field: e.match.dataset.field, value: ""}));

on("click", ".showresponse", e => {
const r = e.match.resp_data;
["permitted", "timestamp"].forEach(key => {
Expand Down
36 changes: 29 additions & 7 deletions modules/http/chan_form.pike
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ constant markdown = #"# Forms for $$channel$$
> ### Edit form
>
> $$formfields$$
> [Link to form](form?form=FORMID :#viewform target=_blank) (only while form is open)<br>
> [View form responses](form?responses=FORMID :#viewresp target=_blank) (opens in new window)<br>
> [Delete form](:#delete_form)
>
Expand Down Expand Up @@ -94,18 +95,19 @@ array formfields = ({
({"is_open", "type=checkbox", "Open form"}),
});

array _element_types = ({
array _element_types = ({ //Search for _element_types in this and the JS to find places to add more
({"twitchid", "Twitch username"}), //If mandatory, will force user to be logged in to submit
({"simple", "Text input"}),
({"paragraph", "Paragraph input"}),
({"address", "Street address"}),
({"radio", "Selection (radio) buttons"}),
({"checkbox", "Check box(es)"}),
({"radio", "Selection (radio) buttons"}), //Should generally be made mandatory
({"checkbox", "Check box(es)"}), //If mandatory, at least one checkbox must be selected (but more than one may be)
});
mapping element_types = (mapping)_element_types;
mapping element_attributes = ([
mapping element_attributes = ([ //Matches _element_types
"simple": (["label": type_string]),
"paragraph": (["label": type_string]),
"checkbox": (["label[]": type_string]),
]);

__async__ mapping(string:mixed) http_request(Protocols.HTTP.Server.Request req) {
Expand Down Expand Up @@ -153,7 +155,7 @@ __async__ mapping(string:mixed) http_request(Protocols.HTTP.Server.Request req)
string formdata = "";
foreach (form->elements, mapping el) {
string|zero elem = 0;
switch (el->type) {
switch (el->type) { //Matches _element_types
case "simple":
elem = sprintf("<label><span>%s</span> <input name=%q></label>",
el->label, "field-" + el->name,
Expand All @@ -164,6 +166,15 @@ __async__ mapping(string:mixed) http_request(Protocols.HTTP.Server.Request req)
el->label, "field-" + el->name,
);
break;
case "checkbox": {
elem = "<ul>";
if (el->label) foreach (el->label; int i; string l)
elem += sprintf("<li><label><input type=checkbox name=%q> %s</label></li>",
"field-" + el->name, l,
);
elem += "</ul>";
break;
}
default: break;
}
if (elem) formdata += sprintf("<section id=%q>%s</section>\n", "field-" + el->name, elem);
Expand Down Expand Up @@ -294,7 +305,7 @@ __async__ void wscmd_add_element(object channel, mapping(string:mixed) conn, map

__async__ void wscmd_edit_element(object channel, mapping(string:mixed) conn, mapping(string:mixed) msg) {
mapping|zero form_data;
if (!intp(msg->idx) || msg->idx < 0 || !stringp(msg->field) || !stringp(msg->value)) return;
if (!intp(msg->idx) || msg->idx < 0 || !stringp(msg->field) || !stringp(msg->value) || msg->field == "") return;
await(G->G->DB->mutate_config(channel->userid, "forms") {mapping cfg = __ARGS__[0];
form_data = cfg->forms[?msg->id]; if (!form_data) return;
if (msg->idx >= sizeof(form_data->elements)) return;
Expand All @@ -309,13 +320,24 @@ __async__ void wscmd_edit_element(object channel, mapping(string:mixed) conn, ma
el->name = msg->value;
form_data = 0; return; //Signal acceptance of the edit
}
else if (msg->field[-1] == ']') {
sscanf(msg->field, "%s[%d]", string basename, int idx);
if (msg->field != sprintf("%s[%d]", basename, idx)) return; //Strict formatting, no extra zeroes or anything
function validator = element_attributes[el->type][basename + "[]"];
if (!validator || !validator(msg->value)) return;
if (!arrayp(el[basename])) el[basename] = ({ });
while (sizeof(el[basename]) <= idx) el[basename] += ({""});
el[basename][idx] = msg->value;
el[basename] -= ({""}); //Set to blank to delete an entry
form_data = 0; return;
}
else if (function validator = element_attributes[el->type][msg->field]) {
if (!validator(msg->value)) return;
el[msg->field] = msg->value;
form_data = 0; return;
}
});
if (form_data) send_updates_all(channel, "");
if (!form_data) send_updates_all(channel, "");
}

__async__ void wscmd_delete_element(object channel, mapping(string:mixed) conn, mapping(string:mixed) msg) {
Expand Down

0 comments on commit d6b8cfa

Please sign in to comment.