PluginProbe ʕ •ᴥ•ʔ
Author Website Templates – Create Writer, Author & Publisher Websites Easily / 1.1.9
Author Website Templates – Create Writer, Author & Publisher Websites Easily v1.1.9
trunk 1.0.3 1.0.4 1.0.5 1.0.6 1.0.7 1.0.8 1.0.9 1.1.0 1.1.1 1.1.2 1.1.3 1.1.4 1.1.5 1.1.6 1.1.7 1.1.8 1.1.9
author-website-templates / src / template-library / LibraryModal.js
author-website-templates / src / template-library Last commit date
LibraryButton.js 2 weeks ago LibraryModal.js 2 weeks ago index.js 2 weeks ago style.scss 2 weeks ago
LibraryModal.js
170 lines
1 import { Modal, Button, TextControl, ButtonGroup } from '@wordpress/components';
2 import { useSelect, useDispatch } from '@wordpress/data';
3 import { parse } from '@wordpress/blocks';
4 import { __ } from '@wordpress/i18n';
5 import { useState, useMemo } from '@wordpress/element';
6 import { Icon, search } from '@wordpress/icons';
7
8 const LibraryModal = ({ isOpen, onClose }) => {
9 const [activeTab, setActiveTab] = useState('sections'); // 'sections' or 'pages'
10 const [activeCategory, setActiveCategory] = useState('all');
11 const [searchQuery, setSearchQuery] = useState('');
12
13 const { patterns } = useSelect((select) => {
14 const allPatterns = select('core').getBlockPatterns() || [];
15
16 const pluginPatterns = allPatterns.filter(p =>
17 p.categories && p.categories.includes('author-website-templates')
18 );
19
20 return { patterns: pluginPatterns };
21 }, []);
22
23 const getPatternImage = (patternName) => {
24 const cleanSlug = patternName.replace('author-website-templates/', '').replace('awt/', '');
25 const baseUrl = window.awtVars?.patternImagesUrl || '';
26
27 return `${baseUrl}${cleanSlug}.jpg`;
28 };
29
30 const handleImageError = (e) => {
31 e.target.style.display = 'none';
32 e.target.nextSibling.style.display = 'flex';
33 };
34 const filteredPatterns = useMemo(() => {
35 return patterns.filter(pattern => {
36 const isFullPage = pattern.keywords?.includes('full-page');
37 const matchesTab = activeTab === 'pages' ? isFullPage : !isFullPage;
38
39 const matchesSearch = pattern.title.toLowerCase().includes(searchQuery.toLowerCase());
40 const matchesCategory = activeCategory === 'all' || pattern.keywords?.includes(activeCategory);
41
42 return matchesTab && matchesSearch && matchesCategory;
43 });
44 }, [patterns, activeTab, searchQuery, activeCategory]);
45
46 const categories = useMemo(() => {
47 const cats = new Set(['all']);
48 patterns.forEach(p => {
49 const isPage = p.keywords?.includes('full-page');
50 if ((activeTab === 'pages' && isPage) || (activeTab === 'sections' && !isPage)) {
51 p.keywords?.forEach(k => k !== 'full-page' && cats.add(k));
52 p.keywords?.forEach(k => k !== 'full-page' && cats.add(k));
53 }
54 });
55 return Array.from(cats);
56 }, [patterns, activeTab]);
57
58 const { insertBlocks } = useDispatch('core/block-editor');
59
60 const importPattern = (pattern) => {
61 const blocks = parse(pattern.content);
62 insertBlocks(blocks);
63 onClose();
64 };
65
66 if (!isOpen) return null;
67
68 return (
69 <Modal
70 title={null}
71 onRequestClose={onClose}
72 className="awt-library-modal"
73 overlayClassName="awt-library-overlay"
74 >
75 <div className="awt-library-header">
76 <div className="awt-header-left">
77 <h2 className="awt-brand">Author Website Templates Library</h2>
78 <div className="awt-tabs">
79 <button
80 className={`awt-tab ${activeTab === 'sections' ? 'active' : ''}`}
81 onClick={() => { setActiveTab('sections'); setActiveCategory('all'); }}
82 >
83 Sections
84 </button>
85 <button
86 className={`awt-tab ${activeTab === 'pages' ? 'active' : ''}`}
87 onClick={() => { setActiveTab('pages'); setActiveCategory('all'); }}
88 >
89 Pages
90 </button>
91 </div>
92 </div>
93 <div className="awt-header-right">
94 <div className="awt-search-wrapper">
95 <Icon icon={search} />
96 <input
97 type="text"
98 placeholder="Search templates..."
99 value={searchQuery}
100 onChange={(e) => setSearchQuery(e.target.value)}
101 />
102 </div>
103 <Button icon="no-alt" onClick={onClose} label="Close" />
104 </div>
105 </div>
106
107 {/* --- Body Area --- */}
108 <div className="awt-library-body">
109
110 {/* Sidebar Filters */}
111 <div className="awt-library-sidebar">
112 <h3>Categories</h3>
113 <ul>
114 {categories.map(cat => (
115 <li key={cat}>
116 <button
117 className={activeCategory === cat ? 'active' : ''}
118 onClick={() => setActiveCategory(cat)}
119 >
120 {cat.charAt(0).toUpperCase() + cat.slice(1)}
121 </button>
122 </li>
123 ))}
124 </ul>
125 </div>
126
127 {/* Main Grid */}
128 <div className="awt-library-content">
129 {filteredPatterns.length > 0 ? (
130 <div className="awt-template-grid">
131 {filteredPatterns.map((pattern) => {
132 const isFullPage = pattern.keywords?.includes('full-page');
133 return (
134 <div key={pattern.name} className="awt-template-card">
135 <div className={`awt-card-preview ${isFullPage ? 'is-full-page' : ''}`}>
136 <img
137 src={getPatternImage(pattern.name)}
138 alt={pattern.title}
139 onError={handleImageError}
140 className="awt-pattern-img"
141 />
142 <div className="placeholder-preview" style={{ display: 'none' }}>
143 {pattern.title.charAt(0)}
144 </div>
145 <div className="awt-card-overlay">
146 <Button variant="primary" onClick={() => importPattern(pattern)}>
147 Insert
148 </Button>
149 </div>
150 </div>
151 <div className="awt-card-footer">
152 <h4>{pattern.title}</h4>
153 {isFullPage && <span className="awt-badge">Page</span>}
154 </div>
155 </div>
156 );
157 })}
158 </div>
159 ) : (
160 <div className="awt-empty-state">
161 <p>No templates found matching your criteria.</p>
162 </div>
163 )}
164 </div>
165 </div>
166 </Modal>
167 );
168 };
169
170 export default LibraryModal;