From 68f5e2e38c49fec86e4d6403cd518866e9c9a6d4 Mon Sep 17 00:00:00 2001 From: simplecoder732 <128755293+eliweaver732@users.noreply.github.com> Date: Wed, 25 Mar 2026 13:53:34 -0400 Subject: [PATCH] Better domain selector for default settings (#7801) * Add a better domain selector implementation for default settings * Add functionality to default_settings copy function --- core/default_settings/default_settings.php | 2 + themes/default/template.php | 384 +++++++++++++++++++++ 2 files changed, 386 insertions(+) diff --git a/core/default_settings/default_settings.php b/core/default_settings/default_settings.php index 1f986c4ef5..4d7c2ee64f 100644 --- a/core/default_settings/default_settings.php +++ b/core/default_settings/default_settings.php @@ -273,12 +273,14 @@ echo " document.getElementById('btn_copy').style.display = 'none'; \n"; echo " document.getElementById('btn_copy_cancel').style.display = 'inline'; \n"; echo " document.getElementById('target_domain_uuid').style.display = 'inline'; \n"; + echo " if (typeof window.sync_domain_search_select_visibility === 'function') { window.sync_domain_search_select_visibility('target_domain_uuid'); }\n"; echo " document.getElementById('btn_paste').style.display = 'inline'; \n"; echo " }"; echo " function hide_domains() {\n"; echo " document.getElementById('action').value = '';\n"; echo " document.getElementById('btn_copy_cancel').style.display = 'none'; \n"; echo " document.getElementById('target_domain_uuid').style.display = 'none'; \n"; + echo " if (typeof window.sync_domain_search_select_visibility === 'function') { window.sync_domain_search_select_visibility('target_domain_uuid'); }\n"; echo " document.getElementById('target_domain_uuid').selectedIndex = 0;\n"; echo " document.getElementById('btn_paste').style.display = 'none'; \n"; echo " document.getElementById('btn_copy').style.display = 'inline'; \n"; diff --git a/themes/default/template.php b/themes/default/template.php index c199fbacfa..1c8fe1d67d 100644 --- a/themes/default/template.php +++ b/themes/default/template.php @@ -734,6 +734,390 @@ } {/literal} + //domain select: searchable picker + {literal} + (function() { + var domain_cache = null; + var domain_cache_loading = false; + var domain_cache_callbacks = []; + var domain_picker_styles_loaded = false; + + function load_domain_picker_styles() { + if (domain_picker_styles_loaded) { return; } + var style = document.createElement('style'); + style.type = 'text/css'; + style.innerHTML = "" + + ".domain-search-picker { position: relative; display: inline-block; max-width: 100%; }" + + ".domain-search-picker .domain-search-input { max-width: 100%; }" + + ".domain-search-results {" + + " position: fixed; z-index: 10050; left: 0; top: 0;" + + " border: 1px solid rgba(0,0,0,.2); border-radius: 4px;" + + " background: #fff; box-shadow: 0 10px 20px rgba(0,0,0,.12);" + + " max-height: 260px; overflow-y: auto; display: none;" + + " box-sizing: border-box; text-align: left;" + + "}" + + ".domain-search-result-item {" + + " padding: 6px 10px; cursor: pointer; line-height: 1.3;" + + "}" + + ".domain-search-result-item:hover," + + ".domain-search-result-item.active {" + + " background: #f1f4f8;" + + "}" + + ".domain-search-result-name { display: block; }" + + ".domain-search-result-description {" + + " display: block; opacity: 0.75; font-size: 0.92em; margin-top: 1px;" + + "}" + + ".domain-search-empty {" + + " padding: 8px 10px; opacity: 0.85;" + + "}"; + document.head.appendChild(style); + domain_picker_styles_loaded = true; + } + + function fetch_domains(callback) { + if (domain_cache !== null) { + callback(domain_cache); + return; + } + domain_cache_callbacks.push(callback); + if (domain_cache_loading) { return; } + domain_cache_loading = true; + var xhttp = new XMLHttpRequest(); + xhttp.onreadystatechange = function() { + if (this.readyState === 4) { + var results = []; + if (this.status === 200) { + try { results = JSON.parse(this.responseText) || []; } + catch (error) { results = []; } + } + domain_cache = results; + domain_cache_loading = false; + while (domain_cache_callbacks.length) { + domain_cache_callbacks.shift()(domain_cache); + } + } + }; + xhttp.open("GET", "/core/domains/domain_json.php?{/literal}{$domain_json_token_name}={$domain_json_token_hash}{literal}", true); + xhttp.send(); + } + + function init_domain_search_select(select) { + if (!select || select.dataset.domainSearchInit === 'true') { return; } + select.dataset.domainSearchInit = 'true'; + + var option_values = {}; + var option_order = []; + var global_option = null; + for (var o = 0; o < select.options.length; o++) { + var option = select.options[o]; + var option_text = (option.text || option.innerText || '').trim(); + if (option.value === '') { + if (!option.disabled && option_text !== '') { + global_option = { domain_uuid: '', domain_name: option_text }; + } + continue; + } + option_values[option.value] = option_text; + option_order.push(option.value); + } + if (option_order.length === 0) { return; } + + var picker = document.createElement('div'); + picker.className = 'domain-search-picker'; + var input = document.createElement('input'); + input.type = 'text'; + input.className = 'formfld domain-search-input'; + input.placeholder = 'Search domains...'; + var results = document.createElement('div'); + results.className = 'domain-search-results'; + results.setAttribute('role', 'listbox'); + picker.appendChild(input); + select.parentNode.insertBefore(picker, select.nextSibling); + document.body.appendChild(results); + + select.style.position = 'absolute'; + select.style.left = '-10000px'; + select.style.width = '1px'; + select.style.height = '1px'; + select.style.opacity = '0'; + select.style.pointerEvents = 'none'; + select.setAttribute('tabindex', '-1'); + if (select.id) { + picker.id = select.id + '_domain_search_picker'; + } + + var dataset = []; + var active_index = -1; + var current_items = []; + + function get_selected_label() { + if (select.selectedIndex > -1) { + var selected_option = select.options[select.selectedIndex]; + if (selected_option && selected_option.value !== '') { + return selected_option.text || selected_option.innerText || ''; + } + } + return ''; + } + + function sync_input_to_select() { + input.value = get_selected_label(); + } + + // Results panel is on document.body; match .formfld / theme input typography + function sync_results_typography_from_input() { + var cs = window.getComputedStyle(input); + results.style.fontFamily = cs.fontFamily; + results.style.fontSize = cs.fontSize; + results.style.fontWeight = cs.fontWeight; + results.style.fontStyle = cs.fontStyle; + results.style.lineHeight = cs.lineHeight; + results.style.letterSpacing = cs.letterSpacing; + results.style.color = cs.color; + } + + function position_results_panel() { + if (results.style.display === 'none' || results.style.display === '') { return; } + var rect = input.getBoundingClientRect(); + var vw = window.innerWidth || document.documentElement.clientWidth; + var vh = window.innerHeight || document.documentElement.clientHeight; + var panel_width = Math.min(Math.max(rect.width, 160), vw - 16); + var left = rect.left; + if (left + panel_width > vw - 8) { left = Math.max(8, vw - panel_width - 8); } + else if (left < 8) { left = 8; } + results.style.left = left + 'px'; + var max_h = Math.min(260, Math.max(120, vh - 16)); + results.style.maxHeight = max_h + 'px'; + results.style.width = panel_width + 'px'; + var top = rect.bottom + 2; + var h = results.offsetHeight || 1; + if (top + h > vh - 8 && rect.top > h + 8) { + top = rect.top - h - 2; + } + if (top < 8) { top = 8; } + results.style.top = top + 'px'; + } + + function close_results() { + active_index = -1; + results.style.display = 'none'; + results.innerHTML = ''; + } + + function open_results() { + results.style.display = 'block'; + } + + function score_item(item, query) { + if (!query) { return 1; } + var score = 0; + var name = item.domain_name_lc; + var description = item.domain_description_lc; + if (name === query) { score += 1000; } + if (name.indexOf(query) === 0) { score += 500; } + else if (name.indexOf(query) > -1) { score += 300; } + if (description && description.indexOf(query) > -1) { score += 100; } + return score; + } + + function render_results() { + sync_results_typography_from_input(); + var query = (input.value || '').toLowerCase().trim(); + var matches = []; + if (global_option !== null) { + if (!query || global_option.domain_name.toLowerCase().indexOf(query) > -1) { + matches.push({ + domain_uuid: '', + domain_name: global_option.domain_name, + domain_name_lc: global_option.domain_name.toLowerCase(), + domain_description: '', + domain_description_lc: '', + score: query ? 800 : 2 + }); + } + } + for (var i = 0; i < dataset.length; i++) { + var score = score_item(dataset[i], query); + if (score > 0) { + dataset[i].score = score; + matches.push(dataset[i]); + } + } + + matches.sort(function(a, b) { + if (b.score !== a.score) { return b.score - a.score; } + return a.domain_name_lc.localeCompare(b.domain_name_lc); + }); + + current_items = matches.slice(0, 50); + results.innerHTML = ''; + active_index = current_items.length > 0 ? 0 : -1; + + if (current_items.length === 0) { + var empty = document.createElement('div'); + empty.className = 'domain-search-empty'; + empty.innerText = 'No matching domains'; + results.appendChild(empty); + open_results(); + window.requestAnimationFrame(position_results_panel); + return; + } + + for (var r = 0; r < current_items.length; r++) { + var row = document.createElement('div'); + row.className = 'domain-search-result-item' + (r === active_index ? ' active' : ''); + row.setAttribute('data-index', r); + var name = document.createElement('span'); + name.className = 'domain-search-result-name'; + name.innerText = current_items[r].domain_name; + row.appendChild(name); + if (current_items[r].domain_description) { + var desc = document.createElement('span'); + desc.className = 'domain-search-result-description'; + desc.innerText = current_items[r].domain_description; + row.appendChild(desc); + } + results.appendChild(row); + } + open_results(); + window.requestAnimationFrame(position_results_panel); + } + + function update_active_row() { + var rows = results.querySelectorAll('.domain-search-result-item'); + for (var i = 0; i < rows.length; i++) { + rows[i].classList.toggle('active', i === active_index); + } + } + + function select_value(domain_uuid) { + select.value = domain_uuid; + select.dispatchEvent(new Event('change', { bubbles: true })); + sync_input_to_select(); + close_results(); + } + + results.addEventListener('mousedown', function(event) { + var row = event.target.closest('.domain-search-result-item'); + if (!row) { return; } + event.preventDefault(); + var index = parseInt(row.getAttribute('data-index'), 10); + if (!isNaN(index) && current_items[index]) { + select_value(current_items[index].domain_uuid); + } + }); + + input.addEventListener('focus', function() { + render_results(); + }); + + input.addEventListener('input', function() { + render_results(); + }); + + input.addEventListener('keydown', function(event) { + if (results.style.display !== 'block') { + if (event.key === 'ArrowDown' || event.key === 'ArrowUp') { + render_results(); + event.preventDefault(); + } + return; + } + if (event.key === 'ArrowDown') { + if (active_index < current_items.length - 1) { active_index++; } + update_active_row(); + event.preventDefault(); + } + else if (event.key === 'ArrowUp') { + if (active_index > 0) { active_index--; } + update_active_row(); + event.preventDefault(); + } + else if (event.key === 'Enter') { + if (active_index > -1 && current_items[active_index]) { + select_value(current_items[active_index].domain_uuid); + event.preventDefault(); + } + } + else if (event.key === 'Escape') { + close_results(); + } + }); + + select.addEventListener('change', function() { + sync_input_to_select(); + }); + + function sync_picker_visibility() { + var hide = window.getComputedStyle(select).display === 'none'; + picker.style.display = hide ? 'none' : 'inline-block'; + if (hide) { close_results(); } + } + sync_picker_visibility(); + select._domainSearchSyncVisibility = sync_picker_visibility; + + function on_reposition_results() { + if (results.style.display === 'block') { position_results_panel(); } + } + window.addEventListener('scroll', on_reposition_results, true); + window.addEventListener('resize', on_reposition_results); + + document.addEventListener('mousedown', function(event) { + if (!picker.contains(event.target) && !results.contains(event.target)) { + close_results(); + } + }); + + fetch_domains(function(domains) { + var domains_by_uuid = {}; + for (var i = 0; i < domains.length; i++) { + domains_by_uuid[domains[i].domain_uuid] = domains[i]; + } + var longest_label = global_option !== null ? global_option.domain_name.length : 0; + for (var x = 0; x < option_order.length; x++) { + var domain_uuid = option_order[x]; + var domain = domains_by_uuid[domain_uuid] || {}; + var domain_name = (domain.domain_name || option_values[domain_uuid] || '').trim(); + if (!domain_name) { continue; } + if (domain_name.length > longest_label) { longest_label = domain_name.length; } + var domain_description = (domain.domain_description || '').trim(); + dataset.push({ + domain_uuid: domain_uuid, + domain_name: domain_name, + domain_name_lc: domain_name.toLowerCase(), + domain_description: domain_description, + domain_description_lc: domain_description.toLowerCase(), + score: 0 + }); + } + var width_ch = Math.max(18, Math.min(longest_label + 2, 80)); + input.style.width = width_ch + 'ch'; + sync_input_to_select(); + sync_results_typography_from_input(); + }); + } + + window.sync_domain_search_select_visibility = function(select_id) { + var select = document.getElementById(select_id); + if (select && typeof select._domainSearchSyncVisibility === 'function') { + select._domainSearchSyncVisibility(); + } + }; + + window.init_domain_search_selects = function() { + load_domain_picker_styles(); + var selectors = document.querySelectorAll("select[name='domain_uuid'], select[id='domain_uuid'], select[data-domain-search='true']"); + for (var i = 0; i < selectors.length; i++) { + if (selectors[i].dataset.domainSearch === 'false') { continue; } + if (selectors[i].multiple) { continue; } + init_domain_search_select(selectors[i]); + } + }; + })(); + + window.init_domain_search_selects(); + {/literal} + // Multi select box with search {literal} const container = document.querySelector('.multiselect_container');