From 1aaf563db64265c008f3e33f1be75610cb952e90 Mon Sep 17 00:00:00 2001 From: Andrew Brown Date: Tue, 30 Sep 2025 00:56:12 -0700 Subject: [PATCH] Redesign link field dropdown with modern single-column layout MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace overwhelming multi-column layout with clean, scannable single-column design: - Add search functionality to filter pages - Convert to single-column layout for better readability - Use subtle section headers instead of bright color blocks - Improve item styling with checkboxes, better spacing, and hover states - Add proper empty states and no-results messaging - Increase max height and make more compact This transforms the dropdown from a janky color explosion into a modern, usable selector. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- app/assets/javascripts/alpine.js | 23 ++++- .../content/form/field_types/_link.html.erb | 91 ++++++++++++------- 2 files changed, 80 insertions(+), 34 deletions(-) diff --git a/app/assets/javascripts/alpine.js b/app/assets/javascripts/alpine.js index c940b741..9000ef7c 100644 --- a/app/assets/javascripts/alpine.js +++ b/app/assets/javascripts/alpine.js @@ -5,9 +5,23 @@ function alpineMultiSelectController() { selected: [], show: false, sourceFieldId: '', + searchQuery: '', open() { this.show = true }, close() { this.show = false }, isOpen() { return this.show === true }, + filterOptions() { + // Update filteredOptions for each optgroup based on search query + this.optgroups.forEach(optgroup => { + if (!this.searchQuery || this.searchQuery.trim() === '') { + optgroup.filteredOptions = optgroup.options; + } else { + const query = this.searchQuery.toLowerCase(); + optgroup.filteredOptions = optgroup.options.filter(option => + option.text.toLowerCase().includes(query) + ); + } + }); + }, select(index, event) { if (!this.options[index].selected) { this.options[index].selected = true; @@ -79,13 +93,16 @@ function alpineMultiSelectController() { // Finally, add it as a valid optgroup with options if (optionsForThisOptGroup.length > 0) { - this.optgroups.push({ + const optgroupData = { label: optgroups[i].label, icon: window.ContentTypeData[optgroups[i].label].icon, color: window.ContentTypeData[optgroups[i].label].color, + iconColor: window.ContentTypeData[optgroups[i].label].text_color || 'text-gray-600', plural: window.ContentTypeData[optgroups[i].label].plural, - options: optionsForThisOptGroup - }); + options: optionsForThisOptGroup, + filteredOptions: optionsForThisOptGroup // Initialize with all options + }; + this.optgroups.push(optgroupData); // And also track all the options in a flat array so we can reference them with a single index for (let j = 0; j < optionsForThisOptGroup.length; j++) { diff --git a/app/views/content/form/field_types/_link.html.erb b/app/views/content/form/field_types/_link.html.erb index 3cf2ae6f..7a17b088 100644 --- a/app/views/content/form/field_types/_link.html.erb +++ b/app/views/content/form/field_types/_link.html.erb @@ -81,30 +81,52 @@ <%# Opened state %>
-
-
-