3.0.3.2
Search box animation
This commit is contained in:
+34
-2
@@ -165,8 +165,8 @@
|
||||
}
|
||||
|
||||
.utci-search-toggle[aria-expanded="true"] {
|
||||
color: #fff;
|
||||
background: #c8922a;
|
||||
color: #c8922a;
|
||||
background: #fef3dc;
|
||||
border-color: #c8922a;
|
||||
}
|
||||
|
||||
@@ -486,6 +486,38 @@ body.tl-scrubbing * { transition: none !important; }
|
||||
left: 0;
|
||||
width: 100%;
|
||||
z-index: 60;
|
||||
/* "Eyes opening": unfurl from the top edge while fading in, so the drop
|
||||
shadow grows with the box instead of flashing in fully-formed. */
|
||||
transform-origin: center center;
|
||||
animation: utci-search-open 0.26s cubic-bezier(0.22, 1, 0.36, 1);
|
||||
}
|
||||
|
||||
@keyframes utci-search-open {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: scaleY(0.05);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: scaleY(1);
|
||||
}
|
||||
}
|
||||
|
||||
/* Reverse the reveal when dismissing — close from the vertical middle and
|
||||
fade out rather than vanishing instantly. */
|
||||
.utci-search-overlay.is-closing {
|
||||
animation: utci-search-close 0.2s cubic-bezier(0.4, 0, 1, 1) forwards;
|
||||
}
|
||||
|
||||
@keyframes utci-search-close {
|
||||
from {
|
||||
opacity: 1;
|
||||
transform: scaleY(1);
|
||||
}
|
||||
to {
|
||||
opacity: 0;
|
||||
transform: scaleY(0.05);
|
||||
}
|
||||
}
|
||||
|
||||
.utci-search-overlay .utci-search {
|
||||
|
||||
+20
-8
@@ -219,6 +219,11 @@ export function UTCIForecast() {
|
||||
// Search is hidden by default and revealed via the magnifier next to the
|
||||
// location. The input then overlays the location line to save space.
|
||||
const [searchOpen, setSearchOpen] = useState(false);
|
||||
// While closing, keep the overlay mounted so it can fade/close out before
|
||||
// unmounting; cleared when the exit animation ends.
|
||||
const [searchClosing, setSearchClosing] = useState(false);
|
||||
const openSearch = () => { setSearchClosing(false); setSearchOpen(true); };
|
||||
const closeSearch = () => { setSearchOpen(false); setSearchClosing(true); };
|
||||
const searchWrapRef = useRef(null);
|
||||
const [colTogglesOpen, setColTogglesOpen] = useState(false);
|
||||
const searchInputRef = useRef(null);
|
||||
@@ -286,12 +291,11 @@ export function UTCIForecast() {
|
||||
if (!searchOpen) return;
|
||||
const onDown = (e) => {
|
||||
if (searchWrapRef.current && !searchWrapRef.current.contains(e.target)) {
|
||||
setSearchOpen(false);
|
||||
setSearchQuery('');
|
||||
closeSearch();
|
||||
}
|
||||
};
|
||||
const onKey = (e) => {
|
||||
if (e.key === 'Escape') { setSearchOpen(false); setSearchQuery(''); }
|
||||
if (e.key === 'Escape') { closeSearch(); }
|
||||
};
|
||||
document.addEventListener('mousedown', onDown);
|
||||
document.addEventListener('keydown', onKey);
|
||||
@@ -625,7 +629,7 @@ export function UTCIForecast() {
|
||||
class="utci-loc-name"
|
||||
aria-label="Search for a town or city"
|
||||
aria-expanded=${searchOpen}
|
||||
onClick=${() => setSearchOpen((v) => !v)}
|
||||
onClick=${() => (searchOpen ? closeSearch() : openSearch())}
|
||||
>
|
||||
<svg class="utci-loc-pin" viewBox="0 0 24 24" width="15" height="15" aria-hidden="true">
|
||||
<path fill="currentColor" d="M12 2c-3.87 0-7 3.13-7 7 0 5.25 7 13 7 13s7-7.75 7-13c0-3.87-3.13-7-7-7z" />
|
||||
@@ -638,7 +642,7 @@ export function UTCIForecast() {
|
||||
class="utci-search-toggle"
|
||||
aria-label="Search for a town or city"
|
||||
aria-expanded=${searchOpen}
|
||||
onClick=${() => setSearchOpen((v) => !v)}
|
||||
onClick=${() => (searchOpen ? closeSearch() : openSearch())}
|
||||
>
|
||||
<svg viewBox="0 0 24 24" width="16" height="16" aria-hidden="true">
|
||||
<circle cx="10.5" cy="10.5" r="6.5" fill="none" stroke="currentColor" stroke-width="2" />
|
||||
@@ -653,8 +657,16 @@ export function UTCIForecast() {
|
||||
<div class=${'utci-fetch-time' + (isStale ? ' is-stale' : '')}>
|
||||
${isStale ? html`<span class="stale-dot" title="Data is older than expected - retrying">● </span>` : ''}Last update: ${fetchedAt.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}
|
||||
</div>`}
|
||||
${searchOpen && html`
|
||||
<div class="utci-search-wrap utci-search-overlay">
|
||||
${(searchOpen || searchClosing) && html`
|
||||
<div
|
||||
class=${'utci-search-wrap utci-search-overlay' + (searchOpen ? '' : ' is-closing')}
|
||||
onAnimationEnd=${(e) => {
|
||||
if (e.target === e.currentTarget && !searchOpen) {
|
||||
setSearchClosing(false);
|
||||
setSearchQuery('');
|
||||
}
|
||||
}}
|
||||
>
|
||||
<input
|
||||
ref=${searchInputRef}
|
||||
class="utci-search"
|
||||
@@ -679,7 +691,7 @@ export function UTCIForecast() {
|
||||
setSearchQuery('');
|
||||
setSearchResults([]);
|
||||
setSelectedDay(0);
|
||||
setSearchOpen(false);
|
||||
closeSearch();
|
||||
}}
|
||||
>
|
||||
<div>${r.name}${r.admin1 ? `, ${r.admin1}` : ''}</div>
|
||||
|
||||
Reference in New Issue
Block a user