MediaWiki:Common.js: Difference between revisions
No edit summary |
No edit summary |
||
| Line 229: | Line 229: | ||
/** | /** | ||
* Quick Search Widget — Custom autocomplete for CRT model lookup | * Quick Search Widget — Custom autocomplete for CRT model lookup | ||
* | * The Main Page wikitext contains the styled container, label, and a | ||
* | * "Loading search…" placeholder (using only MW-allowed HTML tags). | ||
* | * This script replaces the placeholder with the actual input, button, | ||
* and dropdown, then wires up event handlers. | |||
*/ | */ | ||
(function () { | (function () { | ||
| Line 245: | Line 246: | ||
// ── Helpers ───────────────────────────────────────────────────── | // ── Helpers ───────────────────────────────────────────────────── | ||
function navigate(title) { | function navigate(title) { | ||
title = (title || '').trim(); | title = (title || '').trim(); | ||
if (title) window.location.href = | if (title) window.location.href = mw.util.getUrl(title); | ||
} | } | ||
| Line 260: | Line 257: | ||
style.id = 'ecrt-qs-styles'; | style.id = 'ecrt-qs-styles'; | ||
style.textContent = [ | style.textContent = [ | ||
' | /* Container & label — match the server-rendered box */ | ||
'.ecrt-qs-container {', | |||
' background: linear-gradient(135deg, #f0f9ff 0%, #e0f2fe 100%);', | ' background: linear-gradient(135deg, #f0f9ff 0%, #e0f2fe 100%);', | ||
' border: 2px solid #7dd3fc;', | ' border: 2px solid #7dd3fc;', | ||
| Line 274: | Line 272: | ||
' font-size: 0.9rem;', | ' font-size: 0.9rem;', | ||
'}', | '}', | ||
/* Placeholder shown before JS loads */ | |||
'.ecrt-qs-placeholder {', | |||
' padding: 0.625rem 0.875rem;', | |||
' font-size: 0.95rem;', | |||
' border: 1.5px solid #cbd5e1;', | |||
' border-radius: 0.5rem;', | |||
' background: #f1f5f9;', | |||
' color: #94a3b8;', | |||
' font-style: italic;', | |||
'}', | |||
/* Interactive controls */ | |||
'.ecrt-qs-wrap {', | '.ecrt-qs-wrap {', | ||
' position: relative;', | ' position: relative;', | ||
| Line 297: | Line 308: | ||
' box-shadow: 0 0 0 3px rgba(2,132,199,0.15);', | ' box-shadow: 0 0 0 3px rgba(2,132,199,0.15);', | ||
'}', | '}', | ||
'.ecrt-qs-input::placeholder { | '.ecrt-qs-input::placeholder { color: #94a3b8; }', | ||
'.ecrt-qs-btn {', | '.ecrt-qs-btn {', | ||
' padding: 0.625rem 1.25rem;', | ' padding: 0.625rem 1.25rem;', | ||
| Line 354: | Line 363: | ||
'}', | '}', | ||
/* | /* Night-mode overrides */ | ||
'.skin-theme-clientpref-night | '.skin-theme-clientpref-night .ecrt-qs-container {', | ||
' background: linear-gradient(135deg, #0f172a 0%, #1e293b 100%);', | ' background: linear-gradient(135deg, #0f172a 0%, #1e293b 100%);', | ||
' border-color: #334155;', | ' border-color: #334155;', | ||
'}', | '}', | ||
'.skin-theme-clientpref-night .ecrt-qs-label { color: #7dd3fc; }', | '.skin-theme-clientpref-night .ecrt-qs-label { color: #7dd3fc; }', | ||
'.skin-theme-clientpref-night .ecrt-qs-placeholder {', | |||
' background: #0f172a;', | |||
' border-color: #334155;', | |||
' color: #475569;', | |||
'}', | |||
'.skin-theme-clientpref-night .ecrt-qs-input {', | '.skin-theme-clientpref-night .ecrt-qs-input {', | ||
' background: #1e293b;', | ' background: #1e293b;', | ||
| Line 390: | Line 404: | ||
} | } | ||
// ── | // ── Init ──────────────────────────────────────────────────────── | ||
function | function init() { | ||
var container = document.getElementById('crt-quick-search'); | |||
var wrap = document.getElementById('ecrt-qs-controls'); | |||
if (!container || !wrap) return; | |||
injectStyles(); | injectStyles(); | ||
// | // ── Build interactive controls ────────────────────────────── | ||
var row = document.createElement('div'); | var row = document.createElement('div'); | ||
row.className = 'ecrt-qs-row'; | row.className = 'ecrt-qs-row'; | ||
| Line 421: | Line 430: | ||
row.appendChild(btn); | row.appendChild(btn); | ||
var dropdown = document.createElement('div'); | var dropdown = document.createElement('div'); | ||
dropdown.className = 'ecrt-qs-dropdown'; | dropdown.className = 'ecrt-qs-dropdown'; | ||
// Replace placeholder with real controls | |||
wrap.innerHTML = ''; | |||
wrap.appendChild(row); | wrap.appendChild(row); | ||
wrap.appendChild(dropdown); | wrap.appendChild(dropdown); | ||
// ── State ─────────────────────────────────────────────────── | // ── State ─────────────────────────────────────────────────── | ||
var items = []; | var items = []; | ||
var activeIdx = -1; | var activeIdx = -1; | ||
var timer = null; | var timer = null; | ||
function openDropdown() { dropdown.classList.add('is-open'); } | function openDropdown() { dropdown.classList.add('is-open'); } | ||
| Line 439: | Line 447: | ||
dropdown.classList.remove('is-open'); | dropdown.classList.remove('is-open'); | ||
activeIdx = -1; | activeIdx = -1; | ||
} | |||
function setActive(idx) { | |||
var els = dropdown.querySelectorAll('.ecrt-qs-item'); | |||
els.forEach(function (el) { el.classList.remove('is-active'); }); | |||
activeIdx = idx; | |||
if (idx >= 0 && idx < els.length) { | |||
els[idx].classList.add('is-active'); | |||
els[idx].scrollIntoView({ block: 'nearest' }); | |||
} | |||
} | } | ||
| Line 459: | Line 477: | ||
el.className = 'ecrt-qs-item'; | el.className = 'ecrt-qs-item'; | ||
el.textContent = r.title; | el.textContent = r.title; | ||
el.addEventListener('mousedown', function (e) { | el.addEventListener('mousedown', function (e) { | ||
e.preventDefault(); | e.preventDefault(); | ||
navigate(r.title); | navigate(r.title); | ||
| Line 482: | Line 498: | ||
dropdown.appendChild(msg); | dropdown.appendChild(msg); | ||
openDropdown(); | openDropdown(); | ||
} | } | ||
| Line 505: | Line 510: | ||
.then(function (r) { return r.json(); }) | .then(function (r) { return r.json(); }) | ||
.then(function (data) { | .then(function (data) { | ||
if (input.value.trim().toLowerCase() === substr.toLowerCase()) { | if (input.value.trim().toLowerCase() === substr.toLowerCase()) { | ||
renderItems((data && data.pfautocomplete) || []); | renderItems((data && data.pfautocomplete) || []); | ||
| Line 531: | Line 535: | ||
input.addEventListener('keydown', function (e) { | input.addEventListener('keydown', function (e) { | ||
if (!dropdown.classList.contains('is-open') || !items.length) { | if (!dropdown.classList.contains('is-open') || !items.length) { | ||
if (e.key === 'Enter') { | if (e.key === 'Enter') { | ||
e.preventDefault(); | e.preventDefault(); | ||
| Line 566: | Line 569: | ||
}); | }); | ||
document.addEventListener('click', function (e) { | document.addEventListener('click', function (e) { | ||
if (! | if (!container.contains(e.target)) { | ||
closeDropdown(); | closeDropdown(); | ||
} | } | ||
}); | }); | ||
input.addEventListener('focus', function () { | input.addEventListener('focus', function () { | ||
if (items.length && input.value.trim().length >= MIN_CHARS) { | if (items.length && input.value.trim().length >= MIN_CHARS) { | ||
| Line 581: | Line 582: | ||
} | } | ||
// ── | // ── Run ───────────────────────────────────────────────────────── | ||
if (document.readyState === 'loading') { | if (document.readyState === 'loading') { | ||
document.addEventListener('DOMContentLoaded', init); | document.addEventListener('DOMContentLoaded', init); | ||