PluginProbe ʕ •ᴥ•ʔ
Starter Sites & Templates by Neve / trunk
Starter Sites & Templates by Neve vtrunk
1.4.0 1.3.0 1.2.29 1.2.28 1.2.6 1.2.7 1.2.8 1.2.9 trunk 1.0.10 1.0.11 1.0.7 1.0.8 1.0.9 1.1.0 1.1.1 1.1.10 1.1.11 1.1.12 1.1.13 1.1.14 1.1.15 1.1.16 1.1.17 1.1.18 1.1.19 1.1.2 1.1.20 1.1.21 1.1.22 1.1.23 1.1.24 1.1.25 1.1.26 1.1.27 1.1.28 1.1.29 1.1.3 1.1.30 1.1.31 1.1.32 1.1.33 1.1.34 1.1.35 1.1.36 1.1.37 1.1.38 1.1.39 1.1.4 1.1.5 1.1.6 1.1.7 1.1.8 1.1.9 1.2.0 1.2.1 1.2.10 1.2.11 1.2.12 1.2.13 1.2.14 1.2.15 1.2.16 1.2.17 1.2.18 1.2.19 1.2.2 1.2.20 1.2.21 1.2.22 1.2.23 1.2.24 1.2.25 1.2.26 1.2.27 1.2.3 1.2.4 1.2.5
templates-patterns-collection / assets / src / Components / CloudLibrary / Library.js
templates-patterns-collection / assets / src / Components / CloudLibrary Last commit date
DemoSiteTemplatesImport.js 3 years ago FeedbackNotice.js 1 year ago Filters.js 1 year ago ImportTemplatesModal.js 1 year ago Library.js 1 year ago ListItem.js 1 year ago PreviewFrame.js 1 year ago common.js 2 years ago
Library.js
619 lines
1 /* global tiobDash */
2 import classnames from 'classnames';
3 import VizSensor from 'react-visibility-sensor';
4
5 import { chevronLeft, chevronRight, close } from '@wordpress/icons';
6 import { useEffect, useState, Fragment, useContext } from '@wordpress/element';
7 import { withDispatch, withSelect } from '@wordpress/data';
8 import { Spinner, Button, Icon } from '@wordpress/components';
9 import { compose } from '@wordpress/compose';
10 import { __, isRTL } from '@wordpress/i18n';
11
12 import { fetchLibrary } from './common';
13 import ListItem from './ListItem';
14 import Loading from '../Loading';
15 import Filters from './Filters';
16 import PreviewFrame from './PreviewFrame';
17 import ImportTemplatesModal from './ImportTemplatesModal';
18 import Logo from '../Icon';
19 import { LicensePanelContext } from '../LicensePanelContext';
20 import FeedbackNotice from './FeedbackNotice';
21 import {EDITOR_MAP} from '../../utils/common';
22 import EditorSelector from '../EditorSelector';
23
24 const Library = ( {
25 isGeneral,
26 setPreview,
27 setTemplateModal,
28 templateModal,
29 currentTab,
30 userStatus,
31 themeData,
32 } ) => {
33 const [ library, setLibrary ] = useState( {
34 gutenberg: [],
35 elementor: [],
36 } );
37 const [ type, setType ] = useState( 'gutenberg' );
38 const [ toImport, setToImport ] = useState( [] );
39 const [ isGrid, setIsGrid ] = useState( isGeneral );
40 const [ showFSE, setShowFSE ] = useState(
41 tiobDash.isFSETheme
42 ? window?.localStorage?.tpcShowFse === 'true'
43 : false
44 );
45 const [ searchQuery, setSearchQuery ] = useState( '' );
46 const [ currentPage, setCurrentPage ] = useState( {
47 gutenberg: 0,
48 elementor: 0,
49 beaver: 0,
50 } );
51 const [ totalPages, setTotalPages ] = useState( {
52 gutenberg: 0,
53 elementor: 0,
54 beaver: 0,
55 } );
56 const [ isLoading, setLoading ] = useState( false );
57 const [ isSearch, setSearch ] = useState( false );
58 const [ isFetching, setFetching ] = useState( false );
59 const [ previewUrl, setPreviewUrl ] = useState( '' );
60
61 const [ sortingOrder, setSortingOrder ] = useState( {
62 templates: {
63 order: 'DESC',
64 orderby: 'date',
65 },
66 library: {
67 order: 'DESC',
68 orderby: 'date',
69 },
70 } );
71
72
73
74 useEffect( () => {
75 setLoading( true );
76 setSearchQuery( '' );
77
78 setCurrentPage( {
79 gutenberg: 0,
80 elementor: 0,
81 beaver: 0,
82 } );
83
84 setTotalPages( {
85 gutenberg: 0,
86 elementor: 0,
87 beaver: 0,
88 } );
89
90 loadTemplates();
91 }, [ isGeneral, type ] );
92
93 const EDITORS = {
94 gutenberg: {
95 label: __( 'Gutenberg', 'templates-patterns-collection' ),
96 icon: 'gutenberg.jpg',
97 },
98 elementor: {
99 label: __( 'Elementor', 'templates-patterns-collection' ),
100 icon: 'elementor.jpg',
101 },
102 beaver: {
103 label: __( 'Beaver', 'templates-patterns-collection' ),
104 icon: 'beaver.jpg',
105 },
106 };
107
108 const loadTemplates = ( updateItem = {} ) => {
109 const params = {
110 page: currentPage[ type ],
111 per_page: 12,
112 type,
113 ...updateItem,
114 };
115
116 if ( isGeneral ) {
117 params.template_site_slug = 'general';
118 params.premade = true;
119 }
120
121 if ( searchQuery ) {
122 params.search = searchQuery;
123 }
124
125 if ( type === 'gutenberg' && showFSE ) {
126 params.type = [ 'gutenberg', 'fse' ];
127 }
128
129 fetchLibrary( isGeneral, params ).then( ( r ) => {
130 setLibrary( {
131 ...library,
132 [ type ]: r.templates,
133 } );
134 setTotalPages( {
135 ...totalPages,
136 [ type ]: r.total,
137 } );
138 setLoading( false );
139 } );
140 };
141
142 const handlePageChange = async ( index = currentPage[ type ] + 1 ) => {
143 setFetching( true );
144 setCurrentPage( {
145 ...currentPage,
146 [ type ]: index,
147 } );
148
149 const params = {
150 page: index,
151 per_page: 12,
152 type,
153 };
154
155 if ( isGeneral ) {
156 params.template_site_slug = 'general';
157 params.premade = true;
158 }
159
160 if ( searchQuery ) {
161 params.search = searchQuery;
162 }
163
164 await fetchLibrary( isGeneral, params ).then( ( r ) => {
165 setLibrary( {
166 ...library,
167 [ type ]: [ ...library[ type ], ...r.templates ],
168 } );
169 setTotalPages( {
170 ...totalPages,
171 [ type ]: r.total,
172 } );
173 } );
174
175 setFetching( false );
176 };
177
178 const handleSearch = ( query = searchQuery ) => {
179 setLoading( true );
180
181 if ( query ) {
182 setSearch( true );
183 } else {
184 setSearch( false );
185 }
186
187 const params = {
188 search: query,
189 type,
190 ...getOrder(),
191 };
192 if ( isGeneral ) {
193 params.template_site_slug = 'general';
194 params.premade = true;
195 }
196
197 if ( type === 'gutenberg' && showFSE ) {
198 params.type = [ 'gutenberg', 'fse' ];
199 }
200
201 fetchLibrary( isGeneral, params ).then( ( r ) => {
202 setLibrary( {
203 ...library,
204 [ type ]: [ ...r.templates ],
205 } );
206 setTotalPages( {
207 ...totalPages,
208 [ type ]: r.total,
209 } );
210 setLoading( false );
211 } );
212 };
213
214 const handleFSEToggle = () => {
215 setLoading( true );
216
217 const newValue = ! showFSE;
218 setShowFSE( newValue );
219 window.localStorage.setItem( 'tpcShowFse', newValue.toString() );
220
221 const params = {
222 search: searchQuery,
223 type,
224 ...getOrder(),
225 };
226
227 if ( isGeneral ) {
228 params.template_site_slug = 'general';
229 params.premade = true;
230 }
231
232 if ( type === 'gutenberg' && newValue ) {
233 params.type = [ 'gutenberg', 'fse' ];
234 }
235
236 fetchLibrary( isGeneral, params ).then( ( r ) => {
237 setLibrary( {
238 ...library,
239 [ type ]: [ ...r.templates ],
240 } );
241 setTotalPages( {
242 ...totalPages,
243 [ type ]: r.total,
244 } );
245 setLoading( false );
246 } );
247 };
248
249 const handlePreview = ( url ) => {
250 setPreviewUrl( url );
251 setPreview( true );
252 };
253
254 const handleClose = () => {
255 setPreviewUrl( '' );
256 setPreview( false );
257 };
258
259 const handleImport = ( id ) => {
260 setToImport( [ id ] );
261 setTemplateModal( true );
262 };
263
264 const currentPreviewIndex =
265 library[ type ] &&
266 library[ type ].findIndex( ( item ) => item.link === previewUrl );
267 const previewedItem =
268 library[ type ] &&
269 library[ type ].find( ( item ) => previewUrl === item.link );
270 const wrapClasses = classnames( 'cloud-items', {
271 'is-grid': isGrid || ( ! userStatus && ! isGeneral ),
272 'is-dummy': ( ! userStatus && ! isGeneral ) || themeData !== false,
273 } );
274
275 const handlePrevious = () => {
276 let newIndex = currentPreviewIndex - 1;
277 if ( currentPreviewIndex === 0 ) {
278 newIndex = library[ type ].length - 1;
279 }
280 setPreviewUrl( library[ type ][ newIndex ].link );
281 };
282
283 const handleNext = () => {
284 let newIndex = currentPreviewIndex + 1;
285 if ( currentPreviewIndex === library[ type ].length - 1 ) {
286 newIndex = 0;
287 }
288 setPreviewUrl( library[ type ][ newIndex ].link );
289 };
290
291 const getOrder = () => {
292 if ( isGeneral ) {
293 return sortingOrder.templates;
294 }
295
296 return sortingOrder.library;
297 };
298
299 const setSorting = ( order ) => {
300 if ( isGeneral ) {
301 return setSortingOrder( {
302 ...sortingOrder,
303 templates: order,
304 } );
305 }
306
307 return setSortingOrder( {
308 ...sortingOrder,
309 library: order,
310 } );
311 };
312
313 const changeOrder = async ( order ) => {
314 setLoading( true );
315 const params = {
316 search: searchQuery,
317 type,
318 ...order,
319 };
320
321 if ( isGeneral ) {
322 params.template_site_slug = 'general';
323 params.premade = true;
324 }
325
326 fetchLibrary( isGeneral, params ).then( ( r ) => {
327 setLibrary( {
328 ...library,
329 [ type ]: [ ...r.templates ],
330 } );
331 setTotalPages( {
332 ...totalPages,
333 [ type ]: r.total,
334 } );
335 setLoading( false );
336 } );
337 };
338
339 const { upgradeURLTpc } = window.tiobDash;
340
341 const UpsellModal = ( {
342 title,
343 description,
344 showUpgradeBtn = true,
345 showLicenseToggle = true,
346 } ) => {
347 const { setLicenseOpen } = useContext( LicensePanelContext );
348
349 return (
350 <div className={ wrapClasses }>
351 <div className="table">
352 { [ ...Array( 12 ) ].map( ( i, n ) => (
353 <ListItem
354 sortingOrder={ getOrder() }
355 onPreview={ handlePreview }
356 userTemplate={ false }
357 key={ n }
358 item={ {} }
359 grid={ true }
360 />
361 ) ) }
362 </div>
363
364 <div className="upsell-modal-overlay">
365 <div className="upsell-modal">
366 <div className="upsell-modal-content">
367 <div className="info">
368 <h3>{ title }</h3>
369
370 <p
371 dangerouslySetInnerHTML={ {
372 __html: description,
373 } }
374 ></p>
375
376 { showUpgradeBtn && (
377 <Button
378 variant="primary"
379 isPrimary
380 href={ upgradeURLTpc }
381 target="_blank"
382 >
383 { __(
384 'Upgrade to PRO',
385 'templates-patterns-collection'
386 ) }
387 </Button>
388 ) }
389
390 { showLicenseToggle && (
391 <Button isLink style={{ marginLeft: '12px' }} href={ tiobDash?.tiobSettings }>
392 { __( 'I already have a key', 'templates-patterns-collection' ) }
393 </Button>
394 ) }
395 </div>
396 <div className="icon">
397 <Icon icon={ Logo } />
398 </div>
399 </div>
400 </div>
401 </div>
402 </div>
403 );
404 };
405
406 if ( ! userStatus && ! isGeneral ) {
407 return (
408 <UpsellModal
409 title={ __(
410 'Templates Cloud is a PRO Feature',
411 'templates-patterns-collection'
412 ) }
413 description={ __(
414 'Unlock the Templates Cloud features and save your pages or posts in the cloud.',
415 'templates-patterns-collection'
416 ) }
417 />
418 );
419 }
420
421 return (
422 <div className={ wrapClasses }>
423 <>
424 <div style={ { display: "flex" } }>
425 <FeedbackNotice importTemplate={templateModal} />
426 </div>
427 <Filters
428 currentTab={ currentTab }
429 isGrid={ isGrid }
430 showFSE={ showFSE }
431 setShowFSE={ handleFSEToggle }
432 setGrid={ setIsGrid }
433 isSearch={ isSearch }
434 searchQuery={ searchQuery }
435 setSearchQuery={ setSearchQuery }
436 onSearch={ handleSearch }
437 sortingOrder={ getOrder() }
438 setSortingOrder={ setSorting }
439 changeOrder={ changeOrder }
440 type={ type }
441 setType={ setType }
442 />
443 { isLoading && <Loading isGrid={ isGrid } /> }
444 { ! isLoading &&
445 ( library[ type ] && library[ type ].length > 0 ? (
446 <>
447 <div className="table">
448 { library[ type ].map( ( item ) => (
449 <ListItem
450 sortingOrder={ getOrder() }
451 onPreview={ handlePreview }
452 userTemplate={ ! isGeneral }
453 key={ item.template_id }
454 item={ item }
455 loadTemplates={ loadTemplates }
456 onImport={ () => handleImport( item ) }
457 grid={ isGrid }
458 />
459 ) ) }
460 </div>
461
462 <VizSensor
463 onChange={ ( isVisible ) => {
464 if ( ! isVisible ) {
465 return false;
466 }
467
468 if (
469 Number( totalPages[ type ] ) >
470 currentPage[ type ]
471 ) {
472 handlePageChange();
473 }
474 } }
475 >
476 <span
477 style={ {
478 height: 10,
479 width: 10,
480 display: 'block',
481 } }
482 />
483 </VizSensor>
484
485 { isFetching && (
486 <div className="fetching-loader">
487 <Spinner />
488 </div>
489 ) }
490 </>
491 ) : (
492 <div className="empty-information">
493 <img
494 src={
495 window.tiobDash.assets + '/img/layout.jpg'
496 }
497 alt={ __(
498 'No Templates Found',
499 'templates-patterns-collection'
500 ) }
501 />
502 <h3>
503 { __(
504 'There are no templates yet',
505 'templates-patterns-collection'
506 ) }
507 </h3>
508 <p>
509 { __(
510 'You can add a page or post to the cloud by accessing it with the WordPress or Elementor/Beaver editor. Learn more about this in our docs.',
511 'templates-patterns-collection'
512 ) }
513 </p>
514 <Button
515 variant="secondary"
516 isSecondary
517 href="https://docs.themeisle.com/article/1354-neve-template-cloud-library?utm_medium=nevedashboard&utm_source=wpadmin&utm_campaign=templatescloud&utm_content=neve"
518 target="_blank"
519 >
520 { __(
521 'Learn more',
522 'templates-patterns-collection'
523 ) }
524 </Button>
525 </div>
526 ) ) }
527 { previewUrl && (
528 <PreviewFrame
529 previewUrl={ previewUrl }
530 rightButtons={
531 <Button
532 isPrimary
533 onClick={ () => handleImport( previewedItem ) }
534 >
535 { __(
536 'Import Template',
537 'templates-patterns-collection'
538 ) }
539 </Button>
540 }
541 heading={ previewedItem.template_name }
542 leftButtons={
543 <>
544 <Button
545 icon={ close }
546 onClick={ handleClose }
547 />
548 { library[ type ].length > 1 && (
549 <>
550 <Button
551 icon={
552 isRTL()
553 ? chevronRight
554 : chevronLeft
555 }
556 onClick={ handlePrevious }
557 />
558 <Button
559 icon={
560 isRTL()
561 ? chevronLeft
562 : chevronRight
563 }
564 onClick={ handleNext }
565 />
566 </>
567 ) }
568 </>
569 }
570 />
571 ) }
572 </>
573 { templateModal &&
574 toImport &&
575 ! isLoading &&
576 toImport.length > 0 && (
577 <ImportTemplatesModal
578 generalTemplates={ true }
579 isUserTemplate={ ! isGeneral }
580 templatesData={ toImport }
581 />
582 ) }
583 </div>
584 );
585 };
586
587 export default compose(
588 withDispatch( ( dispatch ) => {
589 const {
590 setInstallModalStatus,
591 setTemplateModal,
592 setPreviewStatus,
593 } = dispatch( 'neve-onboarding' );
594
595 return {
596 setPreview: ( status ) => setPreviewStatus( status ),
597 setInstallModal: ( status ) => setInstallModalStatus( status ),
598 setTemplateModal,
599 };
600 } ),
601 withSelect( ( select ) => {
602 const {
603 getTemplateModal,
604 getThemeAction,
605 getCurrentEditor,
606 getCurrentTab,
607 getUserStatus,
608 } = select( 'neve-onboarding' );
609
610 return {
611 templateModal: getTemplateModal(),
612 themeData: getThemeAction() || false,
613 editor: getCurrentEditor(),
614 currentTab: getCurrentTab(),
615 userStatus: getUserStatus(),
616 };
617 } )
618 )( Library );
619