// @jsx h
import { h } from 'dom-chef'
import classnames from 'classnames'
import { loadStyle } from '../helpers'
import { isIosDevice } from './helpers/is-ios-device'
import { whenTransitionEnds } from './helpers/when-transition-ends'
let alreadyRunning = false
const decodeHTMLEntities = (string) => {
var doc = new DOMParser().parseFromString(string, 'text/html')
return doc.documentElement.textContent
}
const store = {}
const cachedFetch = (url, nonce = '') =>
store[url]
? new Promise((resolve) => {
resolve(store[url])
store[url] = store[url].clone()
})
: new Promise((resolve) =>
fetch(url, {
headers: {
'X-WP-Nonce': nonce,
},
}).then((response) => {
resolve(response)
store[url] = response.clone()
})
)
const getPreviewElFor = ({
hasThumbs,
post: {
title: { rendered },
link: href,
_embedded = {},
product_price = 0,
product_status = '',
placeholder_image = null,
},
}) => {
const decodedTitle = decodeHTMLEntities(rendered)
const defaultMediaDetails = {
sizes: {
thumbnail: {
source_url: placeholder_image,
},
},
}
const sizes =
(
_embedded['wp:featuredmedia']?.[0]?.media_details ||
defaultMediaDetails
).sizes || {}
return (
{(_embedded['wp:featuredmedia'] || placeholder_image) &&
hasThumbs && (
current.width <
currentSmallest.width
? current
: currentSmallest,
{
width: 9999999999,
}
).source_url ||
_embedded['wp:featuredmedia'][0]
.source_url,
}}
style={{ aspectRatio: '1/1' }}
/>
)}
{decodedTitle}
{product_price || product_status ? (
{product_price ? (
) : null}
{product_status ? (
) : null}
) : null}
)
}
export const mount = (formEl, args = {}) => {
const clickOutsideHandler = (e) => {
let mode = { mode: 'inline', ...args }.mode
if (mode === 'modal') {
return
}
if (formEl.contains(e.target)) {
return
}
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 (options.postType.includes(':')) {
options.postType.replace('ct_forced_', '')
}
options.productPrice = !!formEl.closest(
'[data-live-results*="product_price"]'
)
options.productStatus = !!formEl.closest(
'[data-live-results*="product_status"]'
)
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'))
let maybeStatusEl = formEl.querySelector('[aria-live]')
if (maybeStatusEl) {
maybeStatusEl.innerHTML = ct_localizations.search_live_no_result
}
return
}
options.queryCategory = formEl.querySelector('[name="ct_tax_query"]')
? formEl.querySelector('[name="ct_tax_query"]').value
: ''
formEl.classList.add('ct-searching')
const params = new URLSearchParams()
params.append('_embed', '1')
params.append('post_type', options.postType)
params.append('per_page', options.perPage)
if (options.productPrice === 'true' || options.productPrice === true) {
params.append('product_price', options.productPrice)
}
if (
options.productStatus === 'true' ||
options.productStatus === true
) {
params.append('product_status', options.productStatus)
}
if (options.queryCategory) {
params.append('ct_tax_query', options.queryCategory)
}
if (ct_localizations.lang) {
params.append('lang', ct_localizations.lang)
}
params.append('search', e.target.value)
cachedFetch(
`${ct_localizations.rest_url}wp/v2/posts${
ct_localizations.rest_url.indexOf('?') > -1 ? '&' : '?'
}${params.toString()}`,
formEl.querySelector('.ct-live-results-nonce')
? formEl.querySelector('.ct-live-results-nonce').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)
}
}
let searchResultsCountElLabel =
ct_localizations.search_live_no_result
if (posts.length > 0 && e.target.value.trim().length > 0) {
searchResultsCountElLabel = (
posts.length > 1
? ct_localizations.search_live_many_results
: ct_localizations.search_live_one_result
).replace('%s', posts.length)
}
let maybeStatusEl = formEl.querySelector('[aria-live]')
if (maybeStatusEl) {
maybeStatusEl.innerHTML = searchResultsCountElLabel
}
if (posts.length > 0 && e.target.value.trim().length > 0) {
let searchResultsEl = (