fabrica

hanna/fabrica

fix(web): show selected labels as chips inside the label dropdown toggle

4bc6c12 · hanna committed on 2026-07-25

Replace the "Select labels" text in the dropdown summary with chips for the
checked labels (restoring the placeholder when none are selected), instead of a
separate chip list beside it.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed by hannaSSH key fingerprint: SHA256:4g9cWhkAAw8gwqhJUcVbSnGEvdGwklW+1Aa/fMUu59k
3 files changed · +36 −33UnifiedSplit
assets/base.css +10 −12
@@ -177,26 +177,24 @@ code, pre, .mono {
177 white-space: nowrap;177 white-space: nowrap;
178}178}
179/* Multi-label dropdown on the new-issue form. */179/* Multi-label dropdown on the new-issue form. */
180.label-dropdown-wrap {
181 display: flex;
182 align-items: center;
183 flex-wrap: wrap;
184 gap: 0.4rem;
185 margin-bottom: 0.5rem;
186}
187.label-dropdown-chips {
188 display: flex;
189 flex-wrap: wrap;
190 gap: 0.35rem;
191}
192.label-dropdown {180.label-dropdown {
193 display: inline-block;181 display: inline-block;
194 position: relative;182 position: relative;
183 margin-bottom: 0.5rem;
195}184}
196.label-dropdown > summary {185.label-dropdown > summary {
197 list-style: none;186 list-style: none;
198 width: auto;187 width: auto;
199 cursor: pointer;188 cursor: pointer;
189 display: inline-flex;
190 align-items: center;
191 gap: 0.4rem;
192}
193.label-dropdown-label {
194 display: inline-flex;
195 align-items: center;
196 flex-wrap: wrap;
197 gap: 0.3rem;
200}198}
201.label-dropdown > summary::-webkit-details-marker {199.label-dropdown > summary::-webkit-details-marker {
202 display: none;200 display: none;
assets/fabrica.js +11 −6
@@ -65,18 +65,23 @@
65 });65 });
66 });66 });
6767
68 // ---- Label dropdown: mirror checked labels into chips ----68 // ---- Label dropdown: show checked labels as chips inside the toggle ----
69 document.querySelectorAll("[data-label-dropdown]").forEach(function (dd) {69 document.querySelectorAll("[data-label-dropdown]").forEach(function (dd) {
70 var chips = dd.parentNode.querySelector("[data-label-chips]");70 var summaryLabel = dd.querySelector("[data-label-summary]");
71 if (!chips) return;71 if (!summaryLabel) return;
72 function sync() {72 function sync() {
73 chips.textContent = "";73 var checked = dd.querySelectorAll('input[type="checkbox"]:checked');
74 dd.querySelectorAll('input[type="checkbox"]:checked').forEach(function (cb) {74 summaryLabel.textContent = "";
75 if (checked.length === 0) {
76 summaryLabel.textContent = "Select labels";
77 return;
78 }
79 checked.forEach(function (cb) {
75 var chip = document.createElement("span");80 var chip = document.createElement("span");
76 chip.className = "label-chip";81 chip.className = "label-chip";
77 chip.style.setProperty("--label-color", cb.getAttribute("data-label-color") || "#888");82 chip.style.setProperty("--label-color", cb.getAttribute("data-label-color") || "#888");
78 chip.textContent = cb.getAttribute("data-label-name") || cb.value;83 chip.textContent = cb.getAttribute("data-label-name") || cb.value;
79 chips.appendChild(chip);84 summaryLabel.appendChild(chip);
80 });85 });
81 }86 }
82 dd.addEventListener("change", sync);87 dd.addEventListener("change", sync);
crates/web/src/issues.rs +15 −15
@@ -504,26 +504,26 @@ pub(crate) fn label_form(
504}504}
505505
506/// A `<details>` dropdown of label checkboxes, for selecting several labels when506/// A `<details>` dropdown of label checkboxes, for selecting several labels when
507/// opening an issue. The checkboxes submit with the surrounding form; JS mirrors507/// opening an issue. The checkboxes submit with the surrounding form; JS replaces
508/// the checked ones into a live chip list beside the dropdown.508/// the "Select labels" text inside the toggle with chips for the checked labels.
509fn label_dropdown(repo_labels: &[Label]) -> Markup {509fn label_dropdown(repo_labels: &[Label]) -> Markup {
510 html! {510 html! {
511 div class="label-dropdown-wrap" {511 details class="menu label-dropdown" data-menu data-label-dropdown {
512 details class="menu label-dropdown" data-menu data-label-dropdown {512 summary class="btn" {
513 summary class="btn" { "Select labels " (icon(Icon::ChevronDown)) }513 span class="label-dropdown-label" data-label-summary { "Select labels" }
514 div class="menu-popover label-menu" {514 (icon(Icon::ChevronDown))
515 @for l in repo_labels {515 }
516 label class="checkbox" {516 div class="menu-popover label-menu" {
517 input type="checkbox" name="label" value=(l.id)517 @for l in repo_labels {
518 data-label-name=(l.name)518 label class="checkbox" {
519 data-label-color=(css_color(&l.color));519 input type="checkbox" name="label" value=(l.id)
520 " " span class="label-swatch" style=(format!("--label-color: {}", css_color(&l.color))) {}520 data-label-name=(l.name)
521 " " (l.name)521 data-label-color=(css_color(&l.color));
522 }522 " " span class="label-swatch" style=(format!("--label-color: {}", css_color(&l.color))) {}
523 " " (l.name)
523 }524 }
524 }525 }
525 }526 }
526 span class="label-dropdown-chips" data-label-chips {}
527 }527 }
528 }528 }
529}529}