| 1 |
import { useEffect, useState, useRef, useMemo } from '@wordpress/element' |
| 2 |
import { __ } from '@wordpress/i18n' |
| 3 |
import classNames from 'classnames' |
| 4 |
import Fuse from 'fuse.js' |
| 5 |
import { useTemplatesStore } from '../state/Templates' |
| 6 |
import { useUserStore } from '../state/User' |
| 7 |
|
| 8 |
const searchMemo = new Map() |
| 9 |
|
| 10 |
export default function SiteTypeSelector({ value, setValue, terms }) { |
| 11 |
const preferredOptionsHistory = useUserStore( |
| 12 |
(state) => |
| 13 |
state.preferredOptionsHistory?.siteType?.filter((t) => t.slug) ?? |
| 14 |
{}, |
| 15 |
) |
| 16 |
const searchParams = useTemplatesStore((state) => state.searchParams) |
| 17 |
const [expanded, setExpanded] = useState(false) |
| 18 |
const searchRef = useRef() |
| 19 |
const [fuzzy, setFuzzy] = useState({}) |
| 20 |
const [tempValue, setTempValue] = useState('') |
| 21 |
const [visibleChoices, setVisibleChoices] = useState([]) |
| 22 |
|
| 23 |
const examples = useMemo(() => { |
| 24 |
return terms |
| 25 |
.filter((t) => t?.featured) |
| 26 |
.sort((a, b) => { |
| 27 |
if (a.slug < b.slug) return -1 |
| 28 |
if (a.slug > b.slug) return 1 |
| 29 |
return 0 |
| 30 |
}) |
| 31 |
}, [terms]) |
| 32 |
|
| 33 |
const updateSearch = (term) => { |
| 34 |
setTempValue(term) |
| 35 |
filter(term) |
| 36 |
} |
| 37 |
|
| 38 |
const filter = (term = '') => { |
| 39 |
if (searchMemo.has(term)) { |
| 40 |
setVisibleChoices(searchMemo.get(term)) |
| 41 |
return |
| 42 |
} |
| 43 |
const results = fuzzy.search(term) |
| 44 |
searchMemo.set( |
| 45 |
term, |
| 46 |
results?.length ? results.map((t) => t.item) : examples, |
| 47 |
) |
| 48 |
setVisibleChoices(searchMemo.get(term)) |
| 49 |
} |
| 50 |
|
| 51 |
const showRecent = () => |
| 52 |
visibleChoices === examples && |
| 53 |
Object.keys(preferredOptionsHistory).length > 0 |
| 54 |
const unknown = value.slug === 'unknown' || !value?.slug |
| 55 |
|
| 56 |
useEffect(() => { |
| 57 |
setFuzzy( |
| 58 |
new Fuse(terms, { |
| 59 |
keys: ['slug', 'title', 'keywords'], |
| 60 |
minMatchCharLength: 2, |
| 61 |
threshold: 0.3, |
| 62 |
}), |
| 63 |
) |
| 64 |
}, [terms]) |
| 65 |
|
| 66 |
useEffect(() => { |
| 67 |
if (!tempValue.length) setVisibleChoices(examples) |
| 68 |
}, [examples, tempValue]) |
| 69 |
|
| 70 |
useEffect(() => { |
| 71 |
expanded && searchRef.current.focus() |
| 72 |
}, [expanded]) |
| 73 |
|
| 74 |
const contentHeader = (description) => { |
| 75 |
return ( |
| 76 |
<> |
| 77 |
<span className="flex flex-col text-left"> |
| 78 |
<span className="text-sm mb-1"> |
| 79 |
{__('Site Type', 'extendify')} |
| 80 |
</span> |
| 81 |
<span className="font-light text-xs">{description}</span> |
| 82 |
</span> |
| 83 |
<span className="flex items-center space-x-4"> |
| 84 |
{unknown && !expanded && ( |
| 85 |
<svg |
| 86 |
className="text-wp-alert-red" |
| 87 |
aria-hidden="true" |
| 88 |
focusable="false" |
| 89 |
width="21" |
| 90 |
height="21" |
| 91 |
viewBox="0 0 21 21" |
| 92 |
fill="none" |
| 93 |
xmlns="http://www.w3.org/2000/svg"> |
| 94 |
<path |
| 95 |
className="stroke-current" |
| 96 |
d="M10.9982 4.05371C7.66149 4.05371 4.95654 6.75866 4.95654 10.0954C4.95654 13.4321 7.66149 16.137 10.9982 16.137C14.3349 16.137 17.0399 13.4321 17.0399 10.0954C17.0399 6.75866 14.3349 4.05371 10.9982 4.05371V4.05371Z" |
| 97 |
strokeWidth="1.25" |
| 98 |
/> |
| 99 |
<path |
| 100 |
className="fill-current" |
| 101 |
d="M10.0205 12.8717C10.0205 12.3287 10.4508 11.8881 10.9938 11.8881C11.5368 11.8881 11.9774 12.3287 11.9774 12.8717C11.9774 13.4147 11.5368 13.8451 10.9938 13.8451C10.4508 13.8451 10.0205 13.4147 10.0205 12.8717Z" |
| 102 |
/> |
| 103 |
<path |
| 104 |
className="fill-current" |
| 105 |
d="M11.6495 10.2591C11.6086 10.6177 11.3524 10.9148 10.9938 10.9148C10.625 10.9148 10.3791 10.6074 10.3483 10.2591L10.0205 7.31855C9.95901 6.81652 10.4918 6.34521 10.9938 6.34521C11.4959 6.34521 12.0286 6.81652 11.9774 7.31855L11.6495 10.2591Z" |
| 106 |
/> |
| 107 |
</svg> |
| 108 |
)} |
| 109 |
<svg |
| 110 |
className={classNames('text-gray-700 stroke-current', { |
| 111 |
'transform rotate-90 -translate-x-1': expanded, |
| 112 |
})} |
| 113 |
aria-hidden="true" |
| 114 |
focusable="false" |
| 115 |
width="8" |
| 116 |
height="13" |
| 117 |
viewBox="0 0 8 13" |
| 118 |
fill="none" |
| 119 |
xmlns="http://www.w3.org/2000/svg"> |
| 120 |
<path |
| 121 |
d="M1.24194 11.5952L6.24194 6.09519L1.24194 0.595215" |
| 122 |
strokeWidth="1.5" |
| 123 |
/> |
| 124 |
</svg> |
| 125 |
</span> |
| 126 |
</> |
| 127 |
) |
| 128 |
} |
| 129 |
|
| 130 |
const choicesList = (choices, title = __('Suggestions', 'extendify')) => { |
| 131 |
if (choices === examples) { |
| 132 |
title = __('Examples', 'extendify') |
| 133 |
} |
| 134 |
return ( |
| 135 |
<> |
| 136 |
<h4 className="mt-4 mb-2 text-left uppercase text-xss text-gray-700 font-medium"> |
| 137 |
{title} |
| 138 |
</h4> |
| 139 |
<ul className="m-0"> |
| 140 |
{choices.map((item) => { |
| 141 |
const label = item?.title ?? item.slug |
| 142 |
const current = |
| 143 |
searchParams?.taxonomies?.siteType?.slug === |
| 144 |
item.slug |
| 145 |
return ( |
| 146 |
<li |
| 147 |
key={item.slug + item?.title} |
| 148 |
className="m-0 mb-1"> |
| 149 |
<button |
| 150 |
type="button" |
| 151 |
className={classNames( |
| 152 |
'w-full text-left text-sm bg-transparent hover:text-wp-theme-500 m-0 pl-0 cursor-pointer', |
| 153 |
{ 'text-gray-800': !current }, |
| 154 |
)} |
| 155 |
onClick={() => { |
| 156 |
setExpanded(false) |
| 157 |
setValue(item) |
| 158 |
}}> |
| 159 |
{label} |
| 160 |
</button> |
| 161 |
</li> |
| 162 |
) |
| 163 |
})} |
| 164 |
</ul> |
| 165 |
</> |
| 166 |
) |
| 167 |
} |
| 168 |
|
| 169 |
return ( |
| 170 |
<div className="w-full bg-extendify-transparent-black rounded"> |
| 171 |
<button |
| 172 |
type="button" |
| 173 |
onClick={() => setExpanded((expanded) => !expanded)} |
| 174 |
className="flex items-center justify-between text-gray-800 button-focus w-full p-4 m-0 cursor-pointer bg-transparent hover:bg-extendify-transparent-black-100 rounded"> |
| 175 |
{contentHeader( |
| 176 |
expanded |
| 177 |
? __('What kind of site is this?', 'extendify') |
| 178 |
: value?.title ?? value.slug ?? 'Unknown', |
| 179 |
)} |
| 180 |
</button> |
| 181 |
{expanded && ( |
| 182 |
<div className="p-4 pt-0 overflow-y-auto max-h-96"> |
| 183 |
<div className="relative my-2"> |
| 184 |
<label htmlFor="site-type-search" className="sr-only"> |
| 185 |
{__('Search', 'extendify')} |
| 186 |
</label> |
| 187 |
<input |
| 188 |
ref={searchRef} |
| 189 |
id="site-type-search" |
| 190 |
value={tempValue ?? ''} |
| 191 |
onChange={(event) => |
| 192 |
updateSearch(event.target.value) |
| 193 |
} |
| 194 |
type="text" |
| 195 |
className="button-focus bg-white border-0 m-0 p-3.5 py-2.5 rounded text-sm w-full" |
| 196 |
placeholder={__('Search', 'extendify')} |
| 197 |
/> |
| 198 |
<svg |
| 199 |
className="absolute top-2 right-2 hidden lg:block pointer-events-none" |
| 200 |
xmlns="http://www.w3.org/2000/svg" |
| 201 |
viewBox="0 0 24 24" |
| 202 |
width="24" |
| 203 |
height="24" |
| 204 |
role="img" |
| 205 |
aria-hidden="true" |
| 206 |
focusable="false"> |
| 207 |
<path d="M13.5 6C10.5 6 8 8.5 8 11.5c0 1.1.3 2.1.9 3l-3.4 3 1 1.1 3.4-2.9c1 .9 2.2 1.4 3.6 1.4 3 0 5.5-2.5 5.5-5.5C19 8.5 16.5 6 13.5 6zm0 9.5c-2.2 0-4-1.8-4-4s1.8-4 4-4 4 1.8 4 4-1.8 4-4 4z"></path> |
| 208 |
</svg> |
| 209 |
</div> |
| 210 |
{tempValue.length > 1 && visibleChoices === examples && ( |
| 211 |
<p className="text-left"> |
| 212 |
{__('Nothing found...', 'extendify')} |
| 213 |
</p> |
| 214 |
)} |
| 215 |
{showRecent() && ( |
| 216 |
<div className="mb-8"> |
| 217 |
{choicesList( |
| 218 |
preferredOptionsHistory, |
| 219 |
__('Recent', 'extendify'), |
| 220 |
)} |
| 221 |
</div> |
| 222 |
)} |
| 223 |
{visibleChoices.length > 0 && ( |
| 224 |
<div>{choicesList(visibleChoices)}</div> |
| 225 |
)} |
| 226 |
{unknown ? null : ( |
| 227 |
<button |
| 228 |
type="button" |
| 229 |
className="mt-4 w-full text-left text-sm bg-transparent hover:text-wp-theme-500 pl-0 cursor-pointer text-wp-theme-500" |
| 230 |
onClick={() => { |
| 231 |
setExpanded(false) |
| 232 |
setValue('Unknown') |
| 233 |
}}> |
| 234 |
{__('Reset', 'extendify')} |
| 235 |
</button> |
| 236 |
)} |
| 237 |
</div> |
| 238 |
)} |
| 239 |
</div> |
| 240 |
) |
| 241 |
} |
| 242 |
|