luci-base: switch from server side to client side widget markup
Do not render standard widgets like checkboxes, select boxes, text input fields etc. on the server side anymore but utilize the ui.js primitives instead. This avoids logic duplication between server side cbi templates and JS widgets in the future. Signed-off-by: Jo-Philipp Wich <jo@mein.io>
This commit is contained in:
parent
808b9f36eb
commit
9c7eb1decd
7 changed files with 108 additions and 177 deletions
|
@ -1347,6 +1347,18 @@ function AbstractValue.deplist2json(self, section, deplist)
|
||||||
return util.serialize_json(deps)
|
return util.serialize_json(deps)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Serialize choices
|
||||||
|
function AbstractValue.choices(self)
|
||||||
|
if type(self.keylist) == "table" and #self.keylist > 0 then
|
||||||
|
local i, k, v = nil, nil, {}
|
||||||
|
for i, k in ipairs(self.keylist) do
|
||||||
|
v[k] = self.vallist[i] or k
|
||||||
|
end
|
||||||
|
return v
|
||||||
|
end
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
-- Generates the unique CBID
|
-- Generates the unique CBID
|
||||||
function AbstractValue.cbid(self, section)
|
function AbstractValue.cbid(self, section)
|
||||||
return "cbid."..self.map.config.."."..section.."."..self.option
|
return "cbid."..self.map.config.."."..section.."."..self.option
|
||||||
|
|
|
@ -1,54 +1,19 @@
|
||||||
<%+cbi/valueheader%>
|
<%+cbi/valueheader%>
|
||||||
|
<div<%=attr("data-ui-widget", luci.util.serialize_json({
|
||||||
<%-
|
"Dropdown", self:cfgvalue(section), self:choices(), {
|
||||||
local selected = { }
|
id = cbid,
|
||||||
|
name = cbid,
|
||||||
if self.multiple then
|
sort = self.keylist,
|
||||||
local val
|
multi = self.multiple,
|
||||||
for val in luci.util.imatch(self:cfgvalue(section)) do
|
datatype = self.datatype,
|
||||||
selected[val] = true
|
optional = self.optional or self.rmempty,
|
||||||
end
|
readonly = self.readonly,
|
||||||
else
|
maxlength = self.maxlength,
|
||||||
selected[self:cfgvalue(section)] = true
|
placeholder = self.placeholder,
|
||||||
end
|
display_items = self.display or self.size or 3,
|
||||||
|
dropdown_items = self.dropdown or self.display or self.size or 5,
|
||||||
if not next(selected) and self.default then
|
custom_placeholder = self.custom or
|
||||||
selected[self.default] = true
|
(self.multiple and translate("Enter custom values") or translate("Enter custom value"))
|
||||||
end
|
}
|
||||||
-%>
|
}))%>></div>
|
||||||
|
|
||||||
<div class="cbi-dropdown"<%=
|
|
||||||
attr("name", cbid) ..
|
|
||||||
attr("display-items", self.display or self.size or 3) ..
|
|
||||||
attr("dropdown-items", self.dropdown or self.display or self.size or 5) ..
|
|
||||||
attr("placeholder", self.placeholder or translate("-- please select --")) ..
|
|
||||||
ifattr(self.multiple, "multiple", "multiple") ..
|
|
||||||
ifattr(self.optional or self.rmempty, "optional", "optional")
|
|
||||||
%>>
|
|
||||||
<ul>
|
|
||||||
<% local i, key; for i, key in pairs(self.keylist) do %>
|
|
||||||
<li<%=
|
|
||||||
attr("data-index", i) ..
|
|
||||||
attr("data-depends", self:deplist2json(section, self.deplist[i])) ..
|
|
||||||
attr("data-value", key) ..
|
|
||||||
ifattr(selected[key], "selected", "selected")
|
|
||||||
%>>
|
|
||||||
<%=pcdata(self.vallist[i])%>
|
|
||||||
</li>
|
|
||||||
<% end %>
|
|
||||||
<% if self.custom then %>
|
|
||||||
<li>
|
|
||||||
<input type="password" style="display:none" />
|
|
||||||
<input class="create-item-input" type="text"<%=
|
|
||||||
attr("placeholder", self.custom ~= true and
|
|
||||||
self.custom or
|
|
||||||
(self.multiple and
|
|
||||||
translate("Enter custom values") or
|
|
||||||
translate("Enter custom value")))
|
|
||||||
%> />
|
|
||||||
</li>
|
|
||||||
<% end %>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<%+cbi/valuefooter%>
|
<%+cbi/valuefooter%>
|
||||||
|
|
|
@ -1,13 +1,12 @@
|
||||||
<%+cbi/valueheader%>
|
<%+cbi/valueheader%>
|
||||||
<div<%=
|
<div<%=attr("data-ui-widget", luci.util.serialize_json({
|
||||||
attr("data-prefix", cbid) ..
|
"DynamicList", self:cfgvalue(section), self:choices(), {
|
||||||
attr("data-browser-path", self.default_path) ..
|
name = cbid,
|
||||||
attr("data-dynlist", luci.util.serialize_json({
|
size = self.size,
|
||||||
self.keylist, self.vallist,
|
sort = self.keylist,
|
||||||
self.datatype, self.optional or self.rmempty
|
datatype = self.datatype,
|
||||||
})) ..
|
optional = self.optional or self.rmempty,
|
||||||
attr("data-values", luci.util.serialize_json(self:cfgvalue(section))) ..
|
placeholder = self.placeholder
|
||||||
ifattr(self.size, "data-size", self.size) ..
|
}
|
||||||
ifattr(self.placeholder, "data-placeholder", self.placeholder)
|
}))%>></div>
|
||||||
%>></div>
|
|
||||||
<%+cbi/valuefooter%>
|
<%+cbi/valuefooter%>
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
<%+cbi/valueheader%>
|
<%+cbi/valueheader%>
|
||||||
<input type="hidden" value="1"<%=
|
<div<%=attr("data-ui-widget", luci.util.serialize_json({
|
||||||
attr("name", "cbi.cbe." .. self.config .. "." .. section .. "." .. self.option)
|
"Checkbox", self:cfgvalue(section) or self.default, {
|
||||||
%> />
|
id = cbid,
|
||||||
<input class="cbi-input-checkbox" data-update="click change" type="checkbox"<%=
|
name = cbid,
|
||||||
attr("id", cbid) .. attr("name", cbid) .. attr("value", self.enabled or 1) ..
|
readonly = self.readonly,
|
||||||
ifattr((self:cfgvalue(section) or self.default) == self.enabled, "checked", "checked")
|
hiddenname = "cbi.cbe." .. self.config .. "." .. section .. "." .. self.option,
|
||||||
%> />
|
value_enabled = self.enabled or 1,
|
||||||
<label<%= attr("for", cbid)%>></label>
|
value_disabled = self.disabled or 0
|
||||||
|
}
|
||||||
|
}))%>></div>
|
||||||
<%+cbi/valuefooter%>
|
<%+cbi/valuefooter%>
|
||||||
|
|
|
@ -1,43 +1,14 @@
|
||||||
<%
|
|
||||||
local i, key
|
|
||||||
local br = self.orientation == "horizontal" and ' ' or '<br />'
|
|
||||||
%>
|
|
||||||
|
|
||||||
<%+cbi/valueheader%>
|
<%+cbi/valueheader%>
|
||||||
<% if self.widget == "select" then %>
|
<div<%=attr("data-ui-widget", luci.util.serialize_json({
|
||||||
<select class="cbi-input-select" data-update="change"<%=
|
"Select", self:cfgvalue(section), self:choices(), {
|
||||||
attr("id", cbid) ..
|
id = cbid,
|
||||||
attr("name", cbid) ..
|
name = cbid,
|
||||||
ifattr(self.size, "size")
|
size = self.size,
|
||||||
%>>
|
sort = self.keylist,
|
||||||
<% for i, key in pairs(self.keylist) do -%>
|
widget = self.widget,
|
||||||
<option<%=
|
datatype = self.datatype,
|
||||||
attr("id", cbid.."-"..key) ..
|
optional = self.optional or self.rmempty,
|
||||||
attr("value", key) ..
|
placeholder = self.placeholder
|
||||||
attr("data-index", i) ..
|
}
|
||||||
attr("data-depends", self:deplist2json(section, self.deplist[i])) ..
|
}))%>></div>
|
||||||
ifattr(tostring(self:cfgvalue(section) or self.default) == key, "selected", "selected")
|
|
||||||
%>><%=pcdata(self.vallist[i])%></option>
|
|
||||||
<%- end %>
|
|
||||||
</select>
|
|
||||||
<% elseif self.widget == "radio" then %>
|
|
||||||
<div>
|
|
||||||
<% for i, key in pairs(self.keylist) do %>
|
|
||||||
<label<%=
|
|
||||||
attr("data-index", i) ..
|
|
||||||
attr("data-depends", self:deplist2json(section, self.deplist[i]))
|
|
||||||
%>>
|
|
||||||
<input class="cbi-input-radio" data-update="click change" type="radio"<%=
|
|
||||||
attr("id", cbid.."-"..key) ..
|
|
||||||
attr("name", cbid) ..
|
|
||||||
attr("value", key) ..
|
|
||||||
ifattr((self:cfgvalue(section) or self.default) == key, "checked", "checked")
|
|
||||||
%> />
|
|
||||||
<label<%= attr("for", cbid.."-"..key)%>></label>
|
|
||||||
<%=pcdata(self.vallist[i])%>
|
|
||||||
</label>
|
|
||||||
<% if i == self.size then write(br) end %>
|
|
||||||
<% end %>
|
|
||||||
</div>
|
|
||||||
<% end %>
|
|
||||||
<%+cbi/valuefooter%>
|
<%+cbi/valuefooter%>
|
||||||
|
|
|
@ -1,43 +1,16 @@
|
||||||
<%
|
|
||||||
local i, key
|
|
||||||
local v = self:valuelist(section) or {}
|
|
||||||
-%>
|
|
||||||
|
|
||||||
<%+cbi/valueheader%>
|
<%+cbi/valueheader%>
|
||||||
<% if self.widget == "select" then %>
|
<div<%=attr("data-ui-widget", luci.util.serialize_json({
|
||||||
<select class="cbi-input-select" multiple="multiple" data-update="click change"<%=
|
"Select", self:cfgvalues(section), self:choices(), {
|
||||||
attr("id", cbid) ..
|
id = cbid,
|
||||||
attr("name", cbid) ..
|
name = cbid,
|
||||||
ifattr(self.size, "size")
|
size = self.size,
|
||||||
%>>
|
sort = self.keylist,
|
||||||
<% for i, key in pairs(self.keylist) do -%>
|
multi = true,
|
||||||
<option<%=
|
widget = self.widget,
|
||||||
attr("id", cbid.."-"..key) ..
|
datatype = self.datatype,
|
||||||
attr("value", key) ..
|
optional = self.optional or self.rmempty,
|
||||||
attr("data-index", i) ..
|
readonly = self.readonly,
|
||||||
attr("data-depends", self:deplist2json(section, self.deplist[i])) ..
|
placeholder = self.placeholder
|
||||||
ifattr(luci.util.contains(v, key), "selected", "selected")
|
}
|
||||||
%>><%=pcdata(self.vallist[i])%></option>
|
}))%>></div>
|
||||||
<%- end %>
|
|
||||||
</select>
|
|
||||||
<% elseif self.widget == "checkbox" then %>
|
|
||||||
<div>
|
|
||||||
<% for i, key in pairs(self.keylist) do %>
|
|
||||||
<label<%=
|
|
||||||
attr("data-index", i) ..
|
|
||||||
attr("data-depends", self:deplist2json(section, self.deplist[i]))
|
|
||||||
%>>
|
|
||||||
<input class="cbi-input-checkbox" type="checkbox" data-update="click change"<%=
|
|
||||||
attr("id", cbid.."-"..key) ..
|
|
||||||
attr("name", cbid) ..
|
|
||||||
attr("value", key) ..
|
|
||||||
ifattr(luci.util.contains(v, key), "checked", "checked")
|
|
||||||
%> />
|
|
||||||
<label<%= attr("for", cbid.."-"..key)%>></label>
|
|
||||||
<%=pcdata(self.vallist[i])%>
|
|
||||||
</label>
|
|
||||||
<% if self.size and (i % self.size) == 0 then write('<br />') end %>
|
|
||||||
<% end %>
|
|
||||||
</div>
|
|
||||||
<% end %>
|
|
||||||
<%+cbi/valuefooter%>
|
<%+cbi/valuefooter%>
|
||||||
|
|
|
@ -1,26 +1,35 @@
|
||||||
<%+cbi/valueheader%>
|
<%+cbi/valueheader%>
|
||||||
<%- if self.password then -%>
|
|
||||||
<input type="password" style="position:absolute; left:-100000px" aria-hidden="true" tabindex="-1"<%=
|
<% local choices = self:choices()
|
||||||
attr("name", "password." .. cbid)
|
if choices then %>
|
||||||
%> />
|
<div<%=attr("data-ui-widget", luci.util.serialize_json({
|
||||||
<%- end -%>
|
"Combobox", self:cfgvalue(section) or self.default, choices, {
|
||||||
<input data-update="change"<%=
|
id = cbid,
|
||||||
attr("id", cbid) ..
|
name = cbid,
|
||||||
attr("name", cbid) ..
|
size = self.size,
|
||||||
attr("type", self.password and "password" or "text") ..
|
sort = self.keylist,
|
||||||
attr("class", self.password and "cbi-input-password" or "cbi-input-text") ..
|
datatype = self.datatype,
|
||||||
attr("value", self:cfgvalue(section) or self.default) ..
|
optional = self.optional or self.rmempty,
|
||||||
ifattr(self.password, "autocomplete", "new-password") ..
|
readonly = self.readonly,
|
||||||
ifattr(self.size, "size") ..
|
maxlength = self.maxlength,
|
||||||
ifattr(self.placeholder, "placeholder") ..
|
placeholder = self.placeholder,
|
||||||
ifattr(self.readonly, "readonly") ..
|
custom_placeholder = self.combobox_manual
|
||||||
ifattr(self.maxlength, "maxlength") ..
|
}
|
||||||
ifattr(self.datatype, "data-type", self.datatype) ..
|
}))%>></div>
|
||||||
ifattr(self.datatype, "data-optional", self.optional or self.rmempty) ..
|
<% else %>
|
||||||
ifattr(self.combobox_manual, "data-manual", self.combobox_manual) ..
|
<div<%=attr("data-ui-widget", luci.util.serialize_json({
|
||||||
ifattr(#self.keylist > 0, "data-choices", { self.keylist, self.vallist })
|
"Textfield", self:cfgvalue(section) or self.default, {
|
||||||
%> />
|
id = cbid,
|
||||||
<%- if self.password then -%>
|
name = cbid,
|
||||||
<button class="cbi-button cbi-button-neutral" title="<%:Reveal/hide password%>" aria-label="<%:Reveal/hide password%>" onclick="var e = this.previousElementSibling; e.type = (e.type === 'password') ? 'text' : 'password'; event.preventDefault()">∗</button>
|
size = self.size,
|
||||||
<% end %>
|
datatype = self.datatype,
|
||||||
|
optional = self.optional or self.rmempty,
|
||||||
|
password = self.password,
|
||||||
|
readonly = self.readonly,
|
||||||
|
maxlength = self.maxlength,
|
||||||
|
placeholder = self.placeholder
|
||||||
|
}
|
||||||
|
}))%>></div>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
<%+cbi/valuefooter%>
|
<%+cbi/valuefooter%>
|
||||||
|
|
Loading…
Reference in a new issue