Offer a long filter list as a popover multi-select instead of a wall of chips
CI / full-gate (push) Successful in 2m51s
CI / full-gate (push) Successful in 2m51s
This commit is contained in:
+1
-1
@@ -14,7 +14,7 @@
|
||||
<p>${t("dashboard.starter.intro")}</p>
|
||||
<p>${t("dashboard.starter.replace")}</p>
|
||||
<pre class="code-block"><code>export default definePlugin({
|
||||
apiVersion: "0.2.0",
|
||||
apiVersion: "0.3.0",
|
||||
// view names plugins/<id>/views/<view>.ejs, rendered in this same shell
|
||||
dashboard: (ctx) => ({ view: "dashboard", data: { /* … */ } }),
|
||||
});</code></pre>
|
||||
|
||||
@@ -2,12 +2,13 @@
|
||||
Filter bar: a real GET form so filtering is server-side and zero-JS. Config:
|
||||
rows: Control[][] rows of controls, laid out left→right
|
||||
pills, clearHref, label, action, applyLabel
|
||||
Control.type ∈ search | segmented | select | chips | daterange | spacer.
|
||||
search { name, placeholder?, value?, label? }
|
||||
segmented { name, legend?, value?, options:{value,label,count?}[] } (radios)
|
||||
select { name, label, value?, options:{value,label}[] }
|
||||
chips { name, legend?, value?:string[], options:{value,label}[] } (checkboxes)
|
||||
daterange { legend?, from:{name,value?,label?}, to:{name,value?,label?} }
|
||||
Control.type ∈ search | segmented | select | chips | multiselect | daterange | spacer.
|
||||
search { name, placeholder?, value?, label? }
|
||||
segmented { name, legend?, value?, options:{value,label,count?}[] } (radios)
|
||||
select { name, label, value?, options:{value,label}[] }
|
||||
chips { name, legend?, value?:string[], options:{value,label}[] } (checkboxes)
|
||||
multiselect { name, legend?, value?:string[], options:{value,label}[] } (checkboxes in a popover)
|
||||
daterange { legend?, from:{name,value?,label?}, to:{name,value?,label?} }
|
||||
The form is a GET, which replaces the whole query string — so the visitor's chosen language rides
|
||||
along as a hidden input, and every href here (pills, clear) is run through localeHref.
|
||||
%><%
|
||||
@@ -34,6 +35,13 @@
|
||||
<span class="filter"><label class="sr-only" for="f-<%= c.name %>"><%= c.label %></label><span class="select"><select id="f-<%= c.name %>" name="<%= c.name %>"><% c.options.forEach((o) => { %><option value="<%= o.value %>"<% if (eq(c.value, o.value)) { %> selected<% } %>><%= o.label %></option><% }) %></select></span></span>
|
||||
<% } else if (c.type === "chips") { -%>
|
||||
<fieldset class="filter-field"><legend class="sr-only"><%= c.legend || c.name %></legend><span class="filter-legend" aria-hidden="true"><%= c.legend || c.name %></span><div class="chips"><% (c.options).forEach((o) => { const on = (c.value || []).map(String).includes(String(o.value)); %><label class="chip"><span class="chip-dot" aria-hidden="true"></span><input type="checkbox" name="<%= c.name %>" value="<%= o.value %>"<% if (on) { %> checked<% } %>><%= o.label %></label><% }) %></div></fieldset>
|
||||
<% } else if (c.type === "multiselect") { const legend = c.legend || c.name; const on = (c.value || []).map(String); -%>
|
||||
<%- include("menu", {
|
||||
id: "f-" + c.name + "-menu",
|
||||
align: "left", kebab: false, up: false, width: null, // explicit: EJS would otherwise inherit the page's own
|
||||
trigger: { class: "btn btn-menu", count: on.length || null, label: on.length ? t("filter.selected", { count: on.length, label: legend }) : null, text: legend },
|
||||
items: [{ group: { legend, name: c.name, options: c.options.map((o) => ({ checked: on.includes(String(o.value)), label: o.label, value: o.value })) } }],
|
||||
}) %>
|
||||
<% } else if (c.type === "daterange") { -%>
|
||||
<fieldset class="filter-field"><legend class="sr-only"><%= c.legend || t("filter.dateRange") %></legend><span class="filter-legend" aria-hidden="true"><%= c.legend || t("filter.dateRange") %></span><div class="daterange"><svg class="ico ico-sm" aria-hidden="true"><use href="#i-cal"/></svg><label class="sr-only" for="f-<%= c.from.name %>"><%= c.from.label || t("filter.from") %></label><input type="date" id="f-<%= c.from.name %>" name="<%= c.from.name %>"<% if (c.from.value) { %> value="<%= c.from.value %>"<% } %>><span class="to" aria-hidden="true"><%= t("filter.toSeparator") %></span><label class="sr-only" for="f-<%= c.to.name %>"><%= c.to.label || t("filter.to") %></label><input type="date" id="f-<%= c.to.name %>" name="<%= c.to.name %>"<% if (c.to.value) { %> value="<%= c.to.value %>"<% } %>></div></fieldset>
|
||||
<% } else if (c.type === "spacer") { -%>
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
Config:
|
||||
id string REQUIRED — the panel's id and the trigger's popovertarget. Name it for what
|
||||
the menu is (`locale-menu`); it must be unique on the page.
|
||||
trigger { class?(="btn", "" ⇒ none) · label?(aria-label) · icon? · text? · html?(raw inner, wins) }
|
||||
trigger { class?(="btn", "" ⇒ none) · label?(aria-label) · icon? · text? · count?(badge) · html?(raw inner, wins) }
|
||||
align? "left" left-align the popover (default right)
|
||||
up? boolean open upward (footer menus)
|
||||
kebab? boolean bare kebab trigger (adds .kebab)
|
||||
@@ -26,7 +26,7 @@
|
||||
const popCls = "menu-pop" + (locals.align === "left" ? " left" : "") + (locals.up ? " up" : "");
|
||||
const width = locals.width;
|
||||
-%>
|
||||
<div class="menu"><button<% if (btnCls) { %> class="<%= btnCls %>"<% } %> type="button" popovertarget="<%= locals.id %>"<% if (trigger.label) { %> aria-label="<%= trigger.label %>"<% } %>><% if (trigger.html != null) { %><%- trigger.html %><% } else { if (trigger.icon) { %><svg class="ico ico-sm"><use href="#<%= trigger.icon %>"/></svg><% } if (trigger.text) { %><%= trigger.text %><% } } %></button><div id="<%= locals.id %>" class="<%= popCls %>" popover<% if (width != null) { %> style="min-width:<%= typeof width === "number" ? width + "px" : width %>"<% } %>>
|
||||
<div class="menu"><button<% if (btnCls) { %> class="<%= btnCls %>"<% } %> type="button" popovertarget="<%= locals.id %>"<% if (trigger.label) { %> aria-label="<%= trigger.label %>"<% } %>><% if (trigger.html != null) { %><%- trigger.html %><% } else { if (trigger.icon) { %><svg class="ico ico-sm"><use href="#<%= trigger.icon %>"/></svg><% } if (trigger.text) { %><%= trigger.text %><% } if (trigger.count != null) { %><span class="badge"><%= trigger.count %></span><% } } %></button><div id="<%= locals.id %>" class="<%= popCls %>" popover<% if (width != null) { %> style="min-width:<%= typeof width === "number" ? width + "px" : width %>"<% } %>>
|
||||
<% items.forEach((it) => { -%>
|
||||
<% if (it.head != null) { -%>
|
||||
<div class="menu-head"><%= it.head %></div>
|
||||
|
||||
Reference in New Issue
Block a user