feat: replace Place dropdowns with search input + forced map view
Hijacks #location-selection in Immich's search filter modal: hides country/state/city comboboxes, injects a text input that opens the live-search overlay directly in map view for pan/zoom exploration. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
38bdcf25dc
commit
40dd22e3ad
|
|
@ -255,11 +255,24 @@
|
|||
}
|
||||
|
||||
// --- Search ---
|
||||
async function doSearch(query) {
|
||||
async function doSearch(query, opts) {
|
||||
if (abortCtrl) abortCtrl.abort();
|
||||
abortCtrl = new AbortController();
|
||||
currentQuery = query;
|
||||
|
||||
if (opts && opts.forceMap) {
|
||||
currentView = 'map';
|
||||
updateToggleButtons();
|
||||
const gridEl = panel ? panel.querySelector('.ls-grid') : null;
|
||||
const mapWrap = panel ? panel.querySelector('.ls-map-wrap') : null;
|
||||
const bodyEl = panel ? panel.querySelector('.ls-body') : null;
|
||||
if (gridEl && mapWrap && bodyEl) {
|
||||
gridEl.style.display = 'none';
|
||||
mapWrap.style.display = '';
|
||||
bodyEl.style.overflowY = 'hidden';
|
||||
}
|
||||
}
|
||||
|
||||
const statusEl = getOrCreatePanel().querySelector('.ls-status');
|
||||
const gridEl = getOrCreatePanel().querySelector('.ls-grid');
|
||||
gridEl.innerHTML = '';
|
||||
|
|
@ -625,6 +638,7 @@
|
|||
|
||||
// Initial scan
|
||||
scanForSearchInputs();
|
||||
hijackLocationSection();
|
||||
|
||||
// Watch for dynamically added inputs (SPA navigation)
|
||||
const observer = new MutationObserver((mutations) => {
|
||||
|
|
@ -638,9 +652,87 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
// Always re-check for the Place section (modal mounts/unmounts)
|
||||
hijackLocationSection();
|
||||
});
|
||||
observer.observe(document.body, { childList: true, subtree: true });
|
||||
|
||||
// --- Replace the search filter modal's Place section (Country/State/City
|
||||
// dropdowns) with a single text input that opens the live-search overlay
|
||||
// directly in map view.
|
||||
function hijackLocationSection() {
|
||||
const section = document.getElementById('location-selection');
|
||||
if (!section || section.dataset.lsHijacked === '1') return;
|
||||
section.dataset.lsHijacked = '1';
|
||||
|
||||
// Hide every original child (heading + combobox grid)
|
||||
for (const child of Array.from(section.children)) {
|
||||
child.style.display = 'none';
|
||||
}
|
||||
|
||||
const wrap = document.createElement('div');
|
||||
wrap.className = 'ls-place-hijack';
|
||||
wrap.innerHTML = `
|
||||
<label class="ls-place-label">Location search</label>
|
||||
<div class="ls-place-row">
|
||||
<input type="search" class="ls-place-input"
|
||||
placeholder="Search a place (city, country, landmark)…"
|
||||
autocomplete="off" />
|
||||
<button type="button" class="ls-place-go">Map it</button>
|
||||
</div>
|
||||
<div class="ls-place-hint">Results open on a map — pan and zoom to explore nearby spots.</div>
|
||||
`;
|
||||
section.appendChild(wrap);
|
||||
|
||||
if (!document.getElementById('ls-place-styles')) {
|
||||
const s = document.createElement('style');
|
||||
s.id = 'ls-place-styles';
|
||||
s.textContent = `
|
||||
.ls-place-hijack { display: flex; flex-direction: column; gap: 6px; }
|
||||
.ls-place-label { font-weight: 600; font-size: 14px; }
|
||||
.ls-place-row { display: flex; gap: 8px; }
|
||||
.ls-place-input {
|
||||
flex: 1; padding: 10px 12px; border-radius: 8px;
|
||||
border: 1px solid var(--immich-ui-gray-400, #bbb);
|
||||
background: var(--immich-bg, transparent); color: inherit;
|
||||
font-size: 14px;
|
||||
}
|
||||
.ls-place-input:focus { outline: 2px solid #e94560; outline-offset: 1px; }
|
||||
.ls-place-go {
|
||||
padding: 0 14px; border-radius: 8px; border: 1px solid #e94560;
|
||||
background: #e94560; color: #fff; font-weight: 600; cursor: pointer;
|
||||
}
|
||||
.ls-place-go:hover { filter: brightness(1.1); }
|
||||
.ls-place-hint { font-size: 12px; opacity: 0.7; }
|
||||
`;
|
||||
document.head.appendChild(s);
|
||||
}
|
||||
|
||||
const input = wrap.querySelector('.ls-place-input');
|
||||
const btn = wrap.querySelector('.ls-place-go');
|
||||
|
||||
const go = () => {
|
||||
const q = input.value.trim();
|
||||
if (q.length < MIN_CHARS) return;
|
||||
doSearch(q, { forceMap: true });
|
||||
};
|
||||
|
||||
input.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'Enter') {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
go();
|
||||
}
|
||||
});
|
||||
btn.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
go();
|
||||
});
|
||||
|
||||
// Autofocus once visible
|
||||
setTimeout(() => { try { input.focus(); } catch {} }, 50);
|
||||
}
|
||||
|
||||
// Global Escape
|
||||
document.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'Escape' && panel?.classList.contains('active')) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue