3.0.3.2
Search box animation
This commit is contained in:
+34
-2
@@ -165,8 +165,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.utci-search-toggle[aria-expanded="true"] {
|
.utci-search-toggle[aria-expanded="true"] {
|
||||||
color: #fff;
|
color: #c8922a;
|
||||||
background: #c8922a;
|
background: #fef3dc;
|
||||||
border-color: #c8922a;
|
border-color: #c8922a;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -486,6 +486,38 @@ body.tl-scrubbing * { transition: none !important; }
|
|||||||
left: 0;
|
left: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
z-index: 60;
|
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 {
|
.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
|
// Search is hidden by default and revealed via the magnifier next to the
|
||||||
// location. The input then overlays the location line to save space.
|
// location. The input then overlays the location line to save space.
|
||||||
const [searchOpen, setSearchOpen] = useState(false);
|
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 searchWrapRef = useRef(null);
|
||||||
const [colTogglesOpen, setColTogglesOpen] = useState(false);
|
const [colTogglesOpen, setColTogglesOpen] = useState(false);
|
||||||
const searchInputRef = useRef(null);
|
const searchInputRef = useRef(null);
|
||||||
@@ -286,12 +291,11 @@ export function UTCIForecast() {
|
|||||||
if (!searchOpen) return;
|
if (!searchOpen) return;
|
||||||
const onDown = (e) => {
|
const onDown = (e) => {
|
||||||
if (searchWrapRef.current && !searchWrapRef.current.contains(e.target)) {
|
if (searchWrapRef.current && !searchWrapRef.current.contains(e.target)) {
|
||||||
setSearchOpen(false);
|
closeSearch();
|
||||||
setSearchQuery('');
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const onKey = (e) => {
|
const onKey = (e) => {
|
||||||
if (e.key === 'Escape') { setSearchOpen(false); setSearchQuery(''); }
|
if (e.key === 'Escape') { closeSearch(); }
|
||||||
};
|
};
|
||||||
document.addEventListener('mousedown', onDown);
|
document.addEventListener('mousedown', onDown);
|
||||||
document.addEventListener('keydown', onKey);
|
document.addEventListener('keydown', onKey);
|
||||||
@@ -625,7 +629,7 @@ export function UTCIForecast() {
|
|||||||
class="utci-loc-name"
|
class="utci-loc-name"
|
||||||
aria-label="Search for a town or city"
|
aria-label="Search for a town or city"
|
||||||
aria-expanded=${searchOpen}
|
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">
|
<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" />
|
<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"
|
class="utci-search-toggle"
|
||||||
aria-label="Search for a town or city"
|
aria-label="Search for a town or city"
|
||||||
aria-expanded=${searchOpen}
|
aria-expanded=${searchOpen}
|
||||||
onClick=${() => setSearchOpen((v) => !v)}
|
onClick=${() => (searchOpen ? closeSearch() : openSearch())}
|
||||||
>
|
>
|
||||||
<svg viewBox="0 0 24 24" width="16" height="16" aria-hidden="true">
|
<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" />
|
<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' : '')}>
|
<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' })}
|
${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>`}
|
</div>`}
|
||||||
${searchOpen && html`
|
${(searchOpen || searchClosing) && html`
|
||||||
<div class="utci-search-wrap utci-search-overlay">
|
<div
|
||||||
|
class=${'utci-search-wrap utci-search-overlay' + (searchOpen ? '' : ' is-closing')}
|
||||||
|
onAnimationEnd=${(e) => {
|
||||||
|
if (e.target === e.currentTarget && !searchOpen) {
|
||||||
|
setSearchClosing(false);
|
||||||
|
setSearchQuery('');
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
<input
|
<input
|
||||||
ref=${searchInputRef}
|
ref=${searchInputRef}
|
||||||
class="utci-search"
|
class="utci-search"
|
||||||
@@ -679,7 +691,7 @@ export function UTCIForecast() {
|
|||||||
setSearchQuery('');
|
setSearchQuery('');
|
||||||
setSearchResults([]);
|
setSearchResults([]);
|
||||||
setSelectedDay(0);
|
setSelectedDay(0);
|
||||||
setSearchOpen(false);
|
closeSearch();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div>${r.name}${r.admin1 ? `, ${r.admin1}` : ''}</div>
|
<div>${r.name}${r.admin1 ? `, ${r.admin1}` : ''}</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user