// @jsx h import { h } from 'dom-chef' import classnames from 'classnames' import { loadStyle } from '../helpers' let alreadyRunning = false const store = {} const cachedFetch = (url) => store[url] ? new Promise((resolve) => { resolve(store[url]) store[url] = store[url].clone() }) : new Promise((resolve) => fetch(url).then((response) => { resolve(response) store[url] = response.clone() }) ) const getPreviewElFor = ({ hasThumbs, post: { title: { rendered }, link: href, _embedded = {}, }, }) => { return ( {_embedded['wp:featuredmedia'] && hasThumbs && (
current.width < currentSmallest.width ? current : currentSmallest, { width: 9999999999, } ).source_url || _embedded['wp:featuredmedia'][0].source_url, }} />
)}
) } export const mount = (formEl, args = {}) => { const clickOutsideHandler = (e) => ({ mode: 'inline', ...args }.mode !== 'modal' && !formEl.contains(e.target) && fadeOutAndRemove(formEl.querySelector('.ct-search-results'))) const maybeEl = formEl.querySelector('input[type="search"]') const options = { postType: 'ct_forced_any', // inline | modal mode: 'inline', perPage: 5, ...args, } if (!maybeEl) { return } options.postType = formEl.querySelector('[name="post_type"]') ? `ct_forced_${formEl.querySelector('[name="post_type"]').value}` : formEl.querySelector('[name="ct_post_type"]') ? `ct_forced_${formEl.querySelector('[name="ct_post_type"]').value}` : 'ct_forced_any' if (!window.fetch) return let listener = debounce((e) => { document.removeEventListener('click', clickOutsideHandler) document.addEventListener('click', clickOutsideHandler) if (e.target.value.trim().length === 0) { fadeOutAndRemove(formEl.querySelector('.ct-search-results')) return } formEl.classList.add('ct-searching') cachedFetch( `${ct_localizations.rest_url}wp/v2/posts${ ct_localizations.rest_url.indexOf('?') > -1 ? '&' : '?' }_embed=1&post_type=${options.postType}&per_page=${ options.perPage }&search=${e.target.value}` ).then((response) => { let totalAmountOfPosts = parseInt( response.headers.get('X-WP-Total'), 10 ) loadStyle(ct_localizations.dynamic_styles.search_lazy).then(() => { response.json().then((posts) => { if (alreadyRunning) { return } formEl.classList.remove('ct-searching') let itHadSearchResultsBefore = !!formEl.querySelector( '.ct-search-results' ) alreadyRunning = true let searchResults = formEl.querySelector( '.ct-search-results' ) let { height: heightBeforeRemoval } = searchResults ? searchResults.getBoundingClientRect() : 0 if ( searchResults && !( e.target.value.trim().length === 0 || posts.length === 0 ) ) { /** * Should just quickly replace the list * when results are available */ searchResults && formEl.removeChild(searchResults) } else { if ( e.target.value.trim().length === 0 || posts.length === 0 ) { fadeOutAndRemove(searchResults) } } if (posts.length > 0 && e.target.value.trim().length > 0) { let searchResultsEl = (
{posts.map((post) => getPreviewElFor({ post, hasThumbs: ( formEl.dataset.liveResults || '' ).indexOf('thumbs') > -1, }) )} {totalAmountOfPosts > options.perPage ? ( {ct_localizations.show_more_text} ) : ( [] )}
) formEl.appendChild(searchResultsEl) if (!itHadSearchResultsBefore) { fadeIn(formEl.querySelector('.ct-search-results')) } else { let searchResults = formEl.querySelector( '.ct-search-results' ) let { height: heightAfterReplace, } = searchResults.getBoundingClientRect() if (heightBeforeRemoval !== heightAfterReplace) { searchResults.style.height = `${heightBeforeRemoval}px` searchResults.classList.add('ct-slide') requestAnimationFrame(() => { searchResults.style.height = `${heightAfterReplace}px` whenTransitionEnds(searchResults, () => { searchResults.removeAttribute('style') searchResults.classList.remove( 'ct-slide' ) }) }) } } if (formEl.querySelector('.ct-search-more')) { formEl .querySelector('.ct-search-more') .addEventListener('click', (e) => { e.preventDefault() formEl.submit() }) } window.scrollTo(0, 0) } alreadyRunning = false }) }) }) }, 200) maybeEl.addEventListener('input', listener) ;({ mode: 'inline', ...args }.mode === 'modal' && maybeEl.addEventListener('blur', (e) => setTimeout(() => listener(e)))) maybeEl.addEventListener('focus', (e) => { listener(e) }) if (maybeEl.value.length > 0) { listener({ target: maybeEl }) } } function fadeOutAndRemove(el) { if (!el) return let { height } = el.getBoundingClientRect() el.classList.add('ct-fade-leave') el.style.height = `${height}px` el.closest('form').classList.remove('ct-has-dropdown') requestAnimationFrame(() => { el.classList.remove('ct-fade-leave') el.classList.add('ct-fade-leave-active') el.style.height = 0 whenTransitionEnds( el, () => el.parentNode && el.parentNode.removeChild(el) ) }) } function whenTransitionEnds(el, cb) { const end = () => { el.removeEventListener('transitionend', onEnd) cb() } const onEnd = (e) => { if (e.target === el) { end() } } el.addEventListener('transitionend', onEnd) } function fadeIn(el) { el.classList.add('ct-fade-enter') let { height } = el.getBoundingClientRect() el.classList.add('ct-fade-leave') el.style.height = 0 el.closest('form').classList.add('ct-has-dropdown') requestAnimationFrame(() => { el.style.height = `${height}px` el.classList.remove('ct-fade-enter') el.classList.add('ct-fade-enter-active') whenTransitionEnds(el, () => el.removeAttribute('style')) }) } function debounce(fn, wait) { var timeout return function () { if (!wait) { return fn.apply(this, arguments) } var context = this var args = arguments clearTimeout(timeout) timeout = setTimeout(function () { timeout = null return fn.apply(context, args) }, wait) } } function values(obj) { var result = [] if (typeof obj == 'object' || typeof obj == 'function') { var keys = Object.keys(obj) var len = keys.length for (var i = 0; i < len; i++) { result.push(obj[keys[i]]) } return result } }