PluginProbe
Extendify / 0.10.2
Extendify v0.10.2
3.2.1 3.2.0 3.1.6 3.1.5 3.1.4 3.1.3 3.1.2 3.1.1 3.1.0 3.0.6 3.0.5 3.0.4 trunk 0.1.0 0.10.0 0.10.1 0.10.2 0.11.0 0.11.1 0.2.0 0.3.0 0.3.1 0.4.0 0.5.0 0.6.0 All 127 releases
extendify / src / Onboarding / pages / SiteTypeSelect.js

SiteTypeSelect.js in Extendify 0.10.2, at src/Onboarding/pages/SiteTypeSelect.js

260 lines 11.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 import { useEffect, useState, useRef } from '@wordpress/element'
2 import { __, sprintf } from '@wordpress/i18n'
3 import { mutate } from 'swr'
4 import { getSiteTypes } from '@onboarding/api/DataApi'
5 import { updateOption } from '@onboarding/api/WPApi'
6 import { useFetch } from '@onboarding/hooks/useFetch'
7 import { PageLayout } from '@onboarding/layouts/PageLayout'
8 import { useUserSelectionStore } from '@onboarding/state/UserSelections'
9 import { pageState } from '@onboarding/state/factory'
10 import { SearchIcon, LeftArrowIcon } from '@onboarding/svg'
11 import {
12 fetcher as styleFetcher,
13 fetchData as styleFetchData,
14 } from './SiteStyle'
15
16 export const fetcher = () => getSiteTypes()
17 export const fetchData = () => ({ key: 'site-types' })
18 export const state = pageState('Site Industry', (set, get) => ({
19 title: __('Site Industry', 'extendify'),
20 default: undefined,
21 showInSidebar: true,
22 ready: false,
23 isDefault: () =>
24 useUserSelectionStore.getState()?.siteType?.slug ===
25 get().default?.slug,
26 }))
27 export const SiteTypeSelect = () => {
28 const siteType = useUserSelectionStore((state) => state.siteType)
29 const feedback = useUserSelectionStore(
30 (state) => state.feedbackMissingSiteType,
31 )
32 const [visibleSiteTypes, setVisibleSiteTypes] = useState([])
33 const [search, setSearch] = useState('')
34 const [showExamples, setShowExamples] = useState(true)
35 const searchRef = useRef(null)
36 const { data: siteTypes, loading } = useFetch(fetchData, fetcher)
37 const showMissingInput = () =>
38 window.extOnbData?.activeTests?.['remove-dont-see-inputs'] === 'A'
39
40 useEffect(() => {
41 state.setState({ ready: !loading })
42 }, [loading])
43
44 useEffect(() => {
45 const raf = requestAnimationFrame(() => searchRef.current?.focus())
46 return () => cancelAnimationFrame(raf)
47 }, [searchRef])
48
49 useEffect(() => {
50 if (loading) return
51 if (siteType?.slug) return
52 const defaultSiteType = siteTypes?.find(
53 (record) => record.slug === 'default',
54 )
55 if (defaultSiteType) {
56 const defaultS = {
57 label: defaultSiteType.title,
58 recordId: defaultSiteType.id,
59 slug: defaultSiteType.slug,
60 }
61 useUserSelectionStore.getState().setSiteType(defaultS)
62 state.setState({ default: defaultS })
63 }
64 }, [loading, siteType?.slug, siteTypes])
65
66 useEffect(() => {
67 if (loading) return
68 if (search?.length > 0) {
69 if (!Array.isArray(siteTypes)) return
70 setVisibleSiteTypes(
71 siteTypes?.filter((option) => {
72 const { title, keywords } = option
73 const s = search?.toLowerCase()
74 if (!s) return true
75 if (title.toLowerCase().indexOf(s) > -1) return true
76 if (!keywords?.length) return false
77 return keywords.find(
78 (value) => value.toLowerCase().indexOf(s) > -1,
79 )
80 }),
81 )
82 return
83 }
84 // If search = '' then show the examples
85 setVisibleSiteTypes(siteTypes?.filter((i) => i?.featured))
86 setShowExamples(true)
87 }, [siteTypes, search, loading])
88
89 useEffect(() => {
90 if (loading) return
91 setVisibleSiteTypes(
92 showExamples
93 ? siteTypes?.filter((i) => i?.featured)
94 : siteTypes?.sort((a, b) => a.title.localeCompare(b.title)),
95 )
96 }, [siteTypes, showExamples, loading])
97
98 useEffect(() => {
99 if (!search) return
100 const timer = setTimeout(() => {
101 useUserSelectionStore.setState({
102 siteTypeSearch: [
103 ...useUserSelectionStore.getState().siteTypeSearch,
104 search,
105 ],
106 })
107 }, 500)
108 return () => clearTimeout(timer)
109 }, [search])
110
111 const selectSiteType = async (optionValue) => {
112 useUserSelectionStore.getState().setSiteType({
113 label: optionValue.title,
114 recordId: optionValue.id,
115 slug: optionValue.slug,
116 styles: optionValue.styles,
117 })
118 await updateOption('extendify_siteType', optionValue)
119 }
120
121 return (
122 <PageLayout>
123 <div>
124 <h1 className="text-3xl text-partner-primary-text mb-4 mt-0">
125 {__('Welcome to your WordPress site', 'extendify')}
126 </h1>
127 <p className="text-base opacity-70 mb-0">
128 {__(
129 'Design and launch your site with this guided experience, or head right into the WordPress dashboard if you know your way around.',
130 'extendify',
131 )}
132 </p>
133 </div>
134 <div className="w-full relative max-w-onboarding-sm mx-auto">
135 <div className="sticky bg-white top-10 z-40 pt-9 pb-3 mb-2">
136 <div className="mx-auto flex justify-between mb-4">
137 <h2 className="text-lg m-0 text-gray-900">
138 {__('What is your site about?', 'extendify')}
139 </h2>
140 {search?.length > 0 ? null : (
141 <button
142 type="button"
143 className="bg-transparent hover:text-partner-primary-bg p-0 text-partner-primary-bg text-xs underline cursor-pointer"
144 onClick={() => {
145 setShowExamples((show) => !show)
146 searchRef.current?.focus()
147 }}>
148 {showExamples
149 ? sprintf(
150 __('Show all %s', 'extendify'),
151 loading ? '...' : siteTypes?.length,
152 )
153 : __('Show less', 'extendify')}
154 </button>
155 )}
156 </div>
157 <div className="mx-auto search-panel flex items-center justify-center relative mb-6">
158 <input
159 ref={searchRef}
160 type="text"
161 className="w-full bg-gray-100 h-12 pl-4 input-focus rounded-none ring-offset-0 focus:bg-white"
162 value={search}
163 onChange={(e) => setSearch(e.target.value)}
164 placeholder={__('Search...', 'extendify')}
165 />
166 <SearchIcon className="icon-search" />
167 </div>
168 {loading && <p>{__('Loading...', 'extendify')}</p>}
169 </div>
170 {visibleSiteTypes?.length > 0 && (
171 <div className="relative">
172 {visibleSiteTypes.map((option) => (
173 <SelectButton
174 key={option.id}
175 selectSiteType={selectSiteType}
176 option={option}
177 />
178 ))}
179 </div>
180 )}
181 {!loading && visibleSiteTypes?.length === 0 && (
182 <div className="mx-auto w-full">
183 <div className="flex items-center justify-between uppercase">
184 {__('No Results', 'extendify')}
185 <button
186 type="button"
187 className="bg-transparent hover:text-partner-primary-bg p-0 text-partner-primary-bg text-xs underline cursor-pointer"
188 onClick={() => {
189 setSearch('')
190 searchRef.current?.focus()
191 }}>
192 {sprintf(
193 __('Show all %s', 'extendify'),
194 loading ? '...' : siteTypes?.length,
195 )}
196 </button>
197 </div>
198 {showMissingInput() && (
199 <>
200 <h2 className="text-lg mt-12 mb-4 text-gray-900">
201 {__(
202 "Don't see what you're looking for?",
203 'extendify',
204 )}
205 </h2>
206 <div className="search-panel flex items-center justify-center relative">
207 <input
208 type="text"
209 className="w-full bg-gray-100 h-12 pl-4 input-focus rounded-none ring-offset-0 focus:bg-white"
210 value={feedback}
211 onChange={(e) =>
212 useUserSelectionStore
213 .getState()
214 .setFeedbackMissingSiteType(
215 e.target.value,
216 )
217 }
218 placeholder={__(
219 'Describe your site...',
220 'extendify',
221 )}
222 />
223 </div>
224 </>
225 )}
226 </div>
227 )}
228 </div>
229 </PageLayout>
230 )
231 }
232
233 const SelectButton = ({ option, selectSiteType }) => {
234 const hoveringTimeout = useRef(0)
235 return (
236 <button
237 onClick={() => {
238 selectSiteType(option)
239 }}
240 onMouseEnter={() => {
241 // Prefetch style templates when hovering over site type
242 window.clearTimeout(hoveringTimeout.current)
243 hoveringTimeout.current = window.setTimeout(() => {
244 const data = () => styleFetchData(option)
245 mutate(data, (cache) => {
246 if (cache?.length) return cache
247 return styleFetcher(data())
248 })
249 }, 100)
250 }}
251 onMouseLeave={() => {
252 window.clearTimeout(hoveringTimeout.current)
253 }}
254 className="flex border border-gray-800 hover:text-partner-primary-bg focus:text-partner-primary-bg items-center justify-between mb-3 p-4 py-3 relative w-full button-focus bg-transparent">
255 <span className="text-left">{option.title}</span>
256 <LeftArrowIcon />
257 </button>
258 )
259 }
260