| 1 |
import { |
| 2 |
useCallback, |
| 3 |
useEffect, |
| 4 |
useState, |
| 5 |
useRef, |
| 6 |
useMemo, |
| 7 |
} from '@wordpress/element' |
| 8 |
import { __, sprintf } from '@wordpress/i18n' |
| 9 |
import { getStyles } from '@onboarding/api/DataApi' |
| 10 |
import { StylePreview } from '@onboarding/components/StyledPreview' |
| 11 |
import { useFetch } from '@onboarding/hooks/useFetch' |
| 12 |
import { useIsMountedLayout } from '@onboarding/hooks/useIsMounted' |
| 13 |
import { PageLayout } from '@onboarding/layouts/PageLayout' |
| 14 |
import { useUserSelectionStore } from '@onboarding/state/UserSelections' |
| 15 |
import { pageState } from '@onboarding/state/factory' |
| 16 |
import { SpinnerIcon } from '@onboarding/svg' |
| 17 |
|
| 18 |
export const fetcher = (params) => getStyles(params) |
| 19 |
export const fetchData = (siteType) => { |
| 20 |
siteType = siteType ?? useUserSelectionStore?.getState().siteType |
| 21 |
return { |
| 22 |
key: 'site-style', |
| 23 |
siteType: siteType?.slug ?? 'default', |
| 24 |
styles: siteType?.styles ?? [], |
| 25 |
} |
| 26 |
} |
| 27 |
export const state = pageState('Design', (set, get) => ({ |
| 28 |
title: __('Design', 'extendify'), |
| 29 |
default: undefined, |
| 30 |
showInSidebar: true, |
| 31 |
ready: false, |
| 32 |
isDefault: () => |
| 33 |
useUserSelectionStore.getState().style?.slug === get().default?.slug, |
| 34 |
})) |
| 35 |
export const SiteStyle = () => { |
| 36 |
const siteType = useUserSelectionStore((state) => state.siteType) |
| 37 |
const { data: styleData, loading } = useFetch(fetchData, fetcher) |
| 38 |
const once = useRef(false) |
| 39 |
const stylesRef = useRef() |
| 40 |
const isMounted = useIsMountedLayout() |
| 41 |
const [styles, setStyles] = useState([]) |
| 42 |
|
| 43 |
useEffect(() => { |
| 44 |
state.setState({ ready: !loading }) |
| 45 |
}, [loading]) |
| 46 |
|
| 47 |
useEffect(() => { |
| 48 |
if (!styleData?.length) return |
| 49 |
;(async () => { |
| 50 |
for (const style of styleData) { |
| 51 |
if (!isMounted.current) return |
| 52 |
setStyles((styles) => [...styles, style]) |
| 53 |
await new Promise((resolve) => setTimeout(resolve, 1000)) |
| 54 |
} |
| 55 |
})() |
| 56 |
}, [styleData, isMounted]) |
| 57 |
|
| 58 |
useEffect(() => { |
| 59 |
if (styles?.length && !useUserSelectionStore.getState().style) { |
| 60 |
useUserSelectionStore.getState().setStyle(styles[0]) |
| 61 |
state.setState({ default: styles[0] }) |
| 62 |
} |
| 63 |
}, [styles]) |
| 64 |
|
| 65 |
useEffect(() => { |
| 66 |
if (!styles?.length || once.current) return |
| 67 |
once.current = true |
| 68 |
// Focus the first style |
| 69 |
stylesRef?.current?.querySelector('[role=button]')?.focus() |
| 70 |
}, [styles]) |
| 71 |
|
| 72 |
return ( |
| 73 |
<PageLayout> |
| 74 |
<div> |
| 75 |
<h1 className="text-3xl text-partner-primary-text mb-4 mt-0"> |
| 76 |
{sprintf( |
| 77 |
__( |
| 78 |
'Now pick a design for your new %s site.', |
| 79 |
'extendify', |
| 80 |
), |
| 81 |
siteType?.label?.toLowerCase(), |
| 82 |
)} |
| 83 |
</h1> |
| 84 |
<p className="text-base opacity-70 mb-0"> |
| 85 |
{__('You can personalize this later.', 'extendify')} |
| 86 |
</p> |
| 87 |
</div> |
| 88 |
<div className="w-full"> |
| 89 |
<h2 className="text-lg m-0 mb-4 text-gray-900"> |
| 90 |
{loading |
| 91 |
? __( |
| 92 |
'Please wait a moment while we generate style previews...', |
| 93 |
'extendify', |
| 94 |
) |
| 95 |
: __('Pick your style', 'extendify')} |
| 96 |
</h2> |
| 97 |
<div |
| 98 |
ref={stylesRef} |
| 99 |
className="flex gap-6 flex-wrap justify-center"> |
| 100 |
{styles?.map((style) => ( |
| 101 |
<StylePreviewWrapper |
| 102 |
key={style.recordId} |
| 103 |
style={style} |
| 104 |
/> |
| 105 |
))} |
| 106 |
{/* Budget skeleton loaders */} |
| 107 |
{styleData?.slice(styles?.length).map((data) => ( |
| 108 |
<div |
| 109 |
key={data.slug} |
| 110 |
style={{ height: 497, width: 352 }} |
| 111 |
className="relative"> |
| 112 |
<div className="bg-gray-50 h-full w-full flex items-center justify-center"> |
| 113 |
<SpinnerIcon className="spin w-8" /> |
| 114 |
</div> |
| 115 |
</div> |
| 116 |
))} |
| 117 |
</div> |
| 118 |
</div> |
| 119 |
</PageLayout> |
| 120 |
) |
| 121 |
} |
| 122 |
|
| 123 |
const StylePreviewWrapper = ({ style }) => { |
| 124 |
const onSelect = useCallback((style) => { |
| 125 |
useUserSelectionStore.getState().setStyle(style) |
| 126 |
}, []) |
| 127 |
const context = useMemo( |
| 128 |
() => ({ |
| 129 |
type: 'style', |
| 130 |
detail: style.slug, |
| 131 |
measure: true, |
| 132 |
}), |
| 133 |
[style], |
| 134 |
) |
| 135 |
return ( |
| 136 |
<div className="relative" style={{ height: 497, width: 352 }}> |
| 137 |
<StylePreview |
| 138 |
style={style} |
| 139 |
context={context} |
| 140 |
onSelect={onSelect} |
| 141 |
active={ |
| 142 |
useUserSelectionStore.getState()?.style?.slug === style.slug |
| 143 |
} |
| 144 |
blockHeight={497} |
| 145 |
/> |
| 146 |
</div> |
| 147 |
) |
| 148 |
} |
| 149 |
|