PluginProbe
Extendify / 0.3.0
Extendify v0.3.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 / components / ImportTemplateBlock.js

ImportTemplateBlock.js in Extendify 0.3.0, at src/components/ImportTemplateBlock.js

173 lines 6.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 import classNames from 'classnames'
2 import { useEffect, useState, useRef, useMemo } from '@wordpress/element'
3 import { __, sprintf } from '@wordpress/i18n'
4 import { BlockPreview } from '@wordpress/block-editor'
5 import { rawHandler } from '@wordpress/blocks'
6 import { AuthorizationCheck, Middleware } from '../middleware'
7 import { injectTemplateBlocks } from '../util/templateInjection'
8 import { useUserStore } from '../state/User'
9 import { useGlobalStore } from '../state/GlobalState'
10 import { Templates as TemplatesApi } from '../api/Templates'
11 import { useIsDevMode } from '../hooks/helpers'
12 import { DevButtonOverlay } from './DevHelpers'
13 import { NoImportModal } from './modals/NoImportModal'
14 import { ProModal } from './modals/ProModal'
15
16 const canImportMiddleware = Middleware([
17 'NeedsRegistrationModal',
18 'hasRequiredPlugins',
19 'hasPluginsActivated',
20 ])
21
22 export function ImportTemplateBlock({ template }) {
23 const importButtonRef = useRef(null)
24 const once = useRef(false)
25 const hasAvailableImports = useUserStore(
26 (state) => state.hasAvailableImports,
27 )
28 const loggedIn = useUserStore((state) => state.apiKey.length)
29 const setOpen = useGlobalStore((state) => state.setOpen)
30 const pushModal = useGlobalStore((state) => state.pushModal)
31 const removeAllModals = useGlobalStore((state) => state.removeAllModals)
32 const blocks = useMemo(
33 () => rawHandler({ HTML: template.fields.code }),
34 [template.fields.code],
35 )
36 const [loaded, setLoaded] = useState(false)
37 const devMode = useIsDevMode()
38
39 const focusTrapInnerBlocks = () => {
40 if (once.current) return
41 once.current = true
42 Array.from(
43 importButtonRef.current.querySelectorAll(
44 'a, button, input, textarea, select, details, [tabindex]:not([tabindex="-1"])',
45 ),
46 ).forEach((el) => el.setAttribute('tabIndex', '-1'))
47 }
48
49 const importTemplates = async () => {
50 await canImportMiddleware.check(template)
51 AuthorizationCheck(canImportMiddleware)
52 .then(() => {
53 setTimeout(() => {
54 injectTemplateBlocks(blocks, template)
55 .then(() => removeAllModals())
56 .then(() => setOpen(false))
57 .then(() => canImportMiddleware.reset())
58 }, 100)
59 })
60 .catch(() => {})
61 }
62
63 const handleKeyDown = (event) => {
64 if (['Enter', 'Space', ' '].includes(event.key)) {
65 event.stopPropagation()
66 event.preventDefault()
67 importTemplate()
68 }
69 }
70
71 const importTemplate = () => {
72 // Make a note that they attempted to import
73 TemplatesApi.maybeImport(template)
74
75 if (template?.fields?.pro && !loggedIn) {
76 pushModal(<ProModal />)
77 return
78 }
79 if (!hasAvailableImports()) {
80 pushModal(<NoImportModal />)
81 return
82 }
83
84 importTemplates()
85 }
86
87 // Trigger resize event on the live previews to add
88 // Grammerly/Loom/etc compatability
89 // TODO: This can probably be removed after WP 5.9
90 useEffect(() => {
91 const rafIds = []
92 const timeouts = []
93 let rafId1, rafId2, rafId3, rafId4
94 rafId1 = window.requestAnimationFrame(() => {
95 rafId2 = window.requestAnimationFrame(() => {
96 importButtonRef.current
97 .querySelectorAll('iframe')
98 .forEach((frame) => {
99 const inner = frame.contentWindow.document.body
100 const rafId = window.requestAnimationFrame(() => {
101 const maybeRoot =
102 inner.querySelector('.is-root-container')
103 if (maybeRoot) {
104 const height = maybeRoot?.offsetHeight
105 if (height) {
106 rafId4 = window.requestAnimationFrame(
107 () => {
108 frame.style.height = height + 'px'
109 },
110 )
111 const id = window.setTimout(() => {
112 frame.style.height = height + 'px'
113 }, 1000)
114 timeouts.push(id)
115 }
116 }
117 frame.contentWindow.dispatchEvent(
118 new Event('resize'),
119 )
120 })
121 rafIds.push(rafId)
122 })
123 rafId3 = window.requestAnimationFrame(() => {
124 window.dispatchEvent(new Event('resize'))
125 setLoaded(true)
126 })
127 })
128 })
129 return () => {
130 ;[...rafIds, rafId1, rafId2, rafId3, rafId4].forEach((id) =>
131 window.cancelAnimationFrame(id),
132 )
133 timeouts.forEach((id) => window.clearTimeout(id))
134 }
135 }, [])
136
137 return (
138 <div className="relative group">
139 <div
140 role="button"
141 tabIndex="0"
142 ref={importButtonRef}
143 aria-label={sprintf(
144 __('Press to import %s', 'extendify'),
145 template?.fields?.type,
146 )}
147 className="mb-6 md:mb-8 cursor-pointer button-focus"
148 onFocus={focusTrapInnerBlocks}
149 onClick={importTemplate}
150 onKeyDown={handleKeyDown}>
151 <div
152 className={classNames('with-light-shadow relative', {
153 [`is-template--${template.fields.status}`]:
154 template?.fields?.status && devMode,
155 })}>
156 <BlockPreview
157 blocks={blocks}
158 live={false}
159 viewportWidth={1400}
160 />
161 </div>
162 </div>
163 {/* Show dev info after the preview is loaded to trigger observer */}
164 {devMode && loaded && <DevButtonOverlay template={template} />}
165 {template?.fields?.pro && (
166 <div className="bg-white bg-wp-theme-500 border font-medium border-none absolute z-20 top-4 right-4 py-1 px-2.5 rounded-md shadow-sm no-underline text-white pointer-events-none">
167 {__('Pro', 'extendify')}
168 </div>
169 )}
170 </div>
171 )
172 }
173