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

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