PluginProbe
Extendify / 0.10.0
Extendify v0.10.0
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.0, at src/Onboarding/pages/SiteTypeSelect.js

259 lines 10.9 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 setVisibleSiteTypes(
70 siteTypes?.filter((option) => {
71 const { title, keywords } = option
72 const s = search?.toLowerCase()
73 if (!s) return true
74 if (title.toLowerCase().indexOf(s) > -1) return true
75 if (!keywords?.length) return false
76 return keywords.find(
77 (value) => value.toLowerCase().indexOf(s) > -1,
78 )
79 }),
80 )
81 return
82 }
83 // If search = '' then show the examples
84 setVisibleSiteTypes(siteTypes?.filter((i) => i.featured))
85 setShowExamples(true)
86 }, [siteTypes, search, loading])
87
88 useEffect(() => {
89 if (loading) return
90 setVisibleSiteTypes(
91 showExamples
92 ? siteTypes.filter((i) => i.featured)
93 : siteTypes.sort((a, b) => a.title.localeCompare(b.title)),
94 )
95 }, [siteTypes, showExamples, loading])
96
97 useEffect(() => {
98 if (!search) return
99 const timer = setTimeout(() => {
100 useUserSelectionStore.setState({
101 siteTypeSearch: [
102 ...useUserSelectionStore.getState().siteTypeSearch,
103 search,
104 ],
105 })
106 }, 500)
107 return () => clearTimeout(timer)
108 }, [search])
109
110 const selectSiteType = async (optionValue) => {
111 useUserSelectionStore.getState().setSiteType({
112 label: optionValue.title,
113 recordId: optionValue.id,
114 slug: optionValue.slug,
115 styles: optionValue.styles,
116 })
117 await updateOption('extendify_siteType', optionValue)
118 }
119
120 return (
121 <PageLayout>
122 <div>
123 <h1 className="text-3xl text-partner-primary-text mb-4 mt-0">
124 {__('Welcome to your WordPress site', 'extendify')}
125 </h1>
126 <p className="text-base opacity-70 mb-0">
127 {__(
128 'Design and launch your site with this guided experience, or head right into the WordPress dashboard if you know your way around.',
129 'extendify',
130 )}
131 </p>
132 </div>
133 <div className="w-full relative max-w-onboarding-sm mx-auto">
134 <div className="sticky bg-white top-10 z-40 pt-9 pb-3 mb-2">
135 <div className="mx-auto flex justify-between mb-4">
136 <h2 className="text-lg m-0 text-gray-900">
137 {__('What is your site about?', 'extendify')}
138 </h2>
139 {search?.length > 0 ? null : (
140 <button
141 type="button"
142 className="bg-transparent hover:text-partner-primary-bg p-0 text-partner-primary-bg text-xs underline cursor-pointer"
143 onClick={() => {
144 setShowExamples((show) => !show)
145 searchRef.current?.focus()
146 }}>
147 {showExamples
148 ? sprintf(
149 __('Show all %s', 'extendify'),
150 loading ? '...' : siteTypes?.length,
151 )
152 : __('Show less', 'extendify')}
153 </button>
154 )}
155 </div>
156 <div className="mx-auto search-panel flex items-center justify-center relative mb-6">
157 <input
158 ref={searchRef}
159 type="text"
160 className="w-full bg-gray-100 h-12 pl-4 input-focus rounded-none ring-offset-0 focus:bg-white"
161 value={search}
162 onChange={(e) => setSearch(e.target.value)}
163 placeholder={__('Search...', 'extendify')}
164 />
165 <SearchIcon className="icon-search" />
166 </div>
167 {loading && <p>{__('Loading...', 'extendify')}</p>}
168 </div>
169 {visibleSiteTypes?.length > 0 && (
170 <div className="relative">
171 {visibleSiteTypes.map((option) => (
172 <SelectButton
173 key={option.id}
174 selectSiteType={selectSiteType}
175 option={option}
176 />
177 ))}
178 </div>
179 )}
180 {!loading && visibleSiteTypes?.length === 0 && (
181 <div className="mx-auto w-full">
182 <div className="flex items-center justify-between uppercase">
183 {__('No Results', 'extendify')}
184 <button
185 type="button"
186 className="bg-transparent hover:text-partner-primary-bg p-0 text-partner-primary-bg text-xs underline cursor-pointer"
187 onClick={() => {
188 setSearch('')
189 searchRef.current?.focus()
190 }}>
191 {sprintf(
192 __('Show all %s', 'extendify'),
193 loading ? '...' : siteTypes?.length,
194 )}
195 </button>
196 </div>
197 {showMissingInput() && (
198 <>
199 <h2 className="text-lg mt-12 mb-4 text-gray-900">
200 {__(
201 "Don't see what you're looking for?",
202 'extendify',
203 )}
204 </h2>
205 <div className="search-panel flex items-center justify-center relative">
206 <input
207 type="text"
208 className="w-full bg-gray-100 h-12 pl-4 input-focus rounded-none ring-offset-0 focus:bg-white"
209 value={feedback}
210 onChange={(e) =>
211 useUserSelectionStore
212 .getState()
213 .setFeedbackMissingSiteType(
214 e.target.value,
215 )
216 }
217 placeholder={__(
218 'Describe your site...',
219 'extendify',
220 )}
221 />
222 </div>
223 </>
224 )}
225 </div>
226 )}
227 </div>
228 </PageLayout>
229 )
230 }
231
232 const SelectButton = ({ option, selectSiteType }) => {
233 const hoveringTimeout = useRef(0)
234 return (
235 <button
236 onClick={() => {
237 selectSiteType(option)
238 }}
239 onMouseEnter={() => {
240 // Prefetch style templates when hovering over site type
241 window.clearTimeout(hoveringTimeout.current)
242 hoveringTimeout.current = window.setTimeout(() => {
243 const data = () => styleFetchData(option)
244 mutate(data, (cache) => {
245 if (cache?.length) return cache
246 return styleFetcher(data())
247 })
248 }, 100)
249 }}
250 onMouseLeave={() => {
251 window.clearTimeout(hoveringTimeout.current)
252 }}
253 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">
254 <span className="text-left">{option.title}</span>
255 <LeftArrowIcon />
256 </button>
257 )
258 }
259