frontblocks-insert-post-option.js
1 month ago
frontblocks-insert-post-option.jsx
1 month ago
frontblocks-insert-post.css
8 months ago
jquery-ui.css
8 months ago
frontblocks-insert-post-option.js
335 lines
| 1 | "use strict"; |
| 2 | |
| 3 | function _toConsumableArray(r) { return _arrayWithoutHoles(r) || _iterableToArray(r) || _unsupportedIterableToArray(r) || _nonIterableSpread(); } |
| 4 | function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } |
| 5 | function _iterableToArray(r) { if ("undefined" != typeof Symbol && null != r[Symbol.iterator] || null != r["@@iterator"]) return Array.from(r); } |
| 6 | function _arrayWithoutHoles(r) { if (Array.isArray(r)) return _arrayLikeToArray(r); } |
| 7 | function _slicedToArray(r, e) { return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest(); } |
| 8 | function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } |
| 9 | function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } } |
| 10 | function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; } |
| 11 | function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } } |
| 12 | function _arrayWithHoles(r) { if (Array.isArray(r)) return r; } |
| 13 | // Insert Post Block Component |
| 14 | var addFilter = wp.hooks.addFilter; |
| 15 | var _wp$element = wp.element, |
| 16 | Fragment = _wp$element.Fragment, |
| 17 | useState = _wp$element.useState, |
| 18 | useEffect = _wp$element.useEffect, |
| 19 | useRef = _wp$element.useRef; |
| 20 | var _wp$blockEditor = wp.blockEditor, |
| 21 | InspectorControls = _wp$blockEditor.InspectorControls, |
| 22 | useBlockProps = _wp$blockEditor.useBlockProps, |
| 23 | RichText = _wp$blockEditor.RichText; |
| 24 | var _wp$components = wp.components, |
| 25 | SelectControl = _wp$components.SelectControl, |
| 26 | TextControl = _wp$components.TextControl, |
| 27 | PanelBody = _wp$components.PanelBody, |
| 28 | Button = _wp$components.Button, |
| 29 | Spinner = _wp$components.Spinner, |
| 30 | Notice = _wp$components.Notice, |
| 31 | Card = _wp$components.Card, |
| 32 | CardBody = _wp$components.CardBody, |
| 33 | CardHeader = _wp$components.CardHeader, |
| 34 | ToggleControl = _wp$components.ToggleControl; |
| 35 | var __ = wp.i18n.__; |
| 36 | var _wp = wp, |
| 37 | apiFetch = _wp.apiFetch; |
| 38 | |
| 39 | // Custom Insert Post Block |
| 40 | function InsertPostBlock(props) { |
| 41 | var attributes = props.attributes, |
| 42 | setAttributes = props.setAttributes; |
| 43 | var _attributes$selectedP = attributes.selectedPostId, |
| 44 | selectedPostId = _attributes$selectedP === void 0 ? 0 : _attributes$selectedP, |
| 45 | _attributes$selectedP2 = attributes.selectedPostType, |
| 46 | selectedPostType = _attributes$selectedP2 === void 0 ? 'post' : _attributes$selectedP2, |
| 47 | _attributes$selectedP3 = attributes.selectedPostTitle, |
| 48 | selectedPostTitle = _attributes$selectedP3 === void 0 ? '' : _attributes$selectedP3, |
| 49 | _attributes$selectedP4 = attributes.selectedPostContent, |
| 50 | selectedPostContent = _attributes$selectedP4 === void 0 ? '' : _attributes$selectedP4, |
| 51 | _attributes$className = attributes.className, |
| 52 | className = _attributes$className === void 0 ? '' : _attributes$className; |
| 53 | var _useState = useState(''), |
| 54 | _useState2 = _slicedToArray(_useState, 2), |
| 55 | searchTerm = _useState2[0], |
| 56 | setSearchTerm = _useState2[1]; |
| 57 | var _useState3 = useState(false), |
| 58 | _useState4 = _slicedToArray(_useState3, 2), |
| 59 | isSearching = _useState4[0], |
| 60 | setIsSearching = _useState4[1]; |
| 61 | var _useState5 = useState(''), |
| 62 | _useState6 = _slicedToArray(_useState5, 2), |
| 63 | searchError = _useState6[0], |
| 64 | setSearchError = _useState6[1]; |
| 65 | var _useState7 = useState(false), |
| 66 | _useState8 = _slicedToArray(_useState7, 2), |
| 67 | showSearch = _useState8[0], |
| 68 | setShowSearch = _useState8[1]; |
| 69 | var searchInputRef = useRef(null); |
| 70 | |
| 71 | // Get available post types |
| 72 | var _useState9 = useState([{ |
| 73 | label: __('Posts', 'frontblocks'), |
| 74 | value: 'post' |
| 75 | }, { |
| 76 | label: __('Pages', 'frontblocks'), |
| 77 | value: 'page' |
| 78 | }]), |
| 79 | _useState0 = _slicedToArray(_useState9, 2), |
| 80 | postTypes = _useState0[0], |
| 81 | setPostTypes = _useState0[1]; |
| 82 | useEffect(function () { |
| 83 | // Get custom post types |
| 84 | apiFetch({ |
| 85 | path: '/wp/v2/types' |
| 86 | }).then(function (types) { |
| 87 | var customTypes = Object.keys(types).filter(function (type) { |
| 88 | return type !== 'post' && type !== 'page'; |
| 89 | }).map(function (type) { |
| 90 | return { |
| 91 | label: types[type].name, |
| 92 | value: type |
| 93 | }; |
| 94 | }); |
| 95 | setPostTypes(function (prev) { |
| 96 | return [].concat(_toConsumableArray(prev), _toConsumableArray(customTypes)); |
| 97 | }); |
| 98 | }).catch(function () { |
| 99 | // If API fails, continue with default post types |
| 100 | }); |
| 101 | }, []); |
| 102 | |
| 103 | // Initialize jQuery autocomplete when component mounts or search is shown |
| 104 | useEffect(function () { |
| 105 | if (showSearch && searchInputRef.current && typeof jQuery !== 'undefined') { |
| 106 | var $input = jQuery(searchInputRef.current); |
| 107 | |
| 108 | // Check if autocomplete is already initialized before destroying |
| 109 | if ($input.hasClass('ui-autocomplete-input')) { |
| 110 | try { |
| 111 | $input.autocomplete('destroy'); |
| 112 | } catch (e) { |
| 113 | // If destroy fails, remove the autocomplete class manually |
| 114 | $input.removeClass('ui-autocomplete-input'); |
| 115 | } |
| 116 | } |
| 117 | |
| 118 | // Initialize autocomplete |
| 119 | $input.autocomplete({ |
| 120 | source: function source(request, response) { |
| 121 | if (request.term.length < 2) { |
| 122 | response([]); |
| 123 | return; |
| 124 | } |
| 125 | setIsSearching(true); |
| 126 | setSearchError(''); |
| 127 | var formData = new FormData(); |
| 128 | formData.append('action', 'frbl_search_posts'); |
| 129 | formData.append('nonce', frblInsertPost.nonce); |
| 130 | formData.append('search', request.term); |
| 131 | formData.append('post_type', selectedPostType); |
| 132 | fetch(frblInsertPost.ajaxUrl, { |
| 133 | method: 'POST', |
| 134 | body: formData |
| 135 | }).then(function (response) { |
| 136 | return response.json(); |
| 137 | }).then(function (data) { |
| 138 | setIsSearching(false); |
| 139 | if (data.success) { |
| 140 | var results = data.data.map(function (post) { |
| 141 | return { |
| 142 | label: "".concat(post.title, " (").concat(post.type, ")"), |
| 143 | value: post.title, |
| 144 | post: post |
| 145 | }; |
| 146 | }); |
| 147 | response(results); |
| 148 | } else { |
| 149 | setSearchError(data.data || __('Search failed', 'frontblocks')); |
| 150 | response([]); |
| 151 | } |
| 152 | }).catch(function (error) { |
| 153 | setIsSearching(false); |
| 154 | setSearchError(__('Search failed. Please try again.', 'frontblocks')); |
| 155 | response([]); |
| 156 | }); |
| 157 | }, |
| 158 | minLength: 2, |
| 159 | delay: 300, |
| 160 | select: function select(event, ui) { |
| 161 | selectPost(ui.item.post); |
| 162 | return false; |
| 163 | }, |
| 164 | open: function open() { |
| 165 | jQuery(this).autocomplete('widget').css('z-index', 999999); |
| 166 | } |
| 167 | }); |
| 168 | |
| 169 | // Cleanup function |
| 170 | return function () { |
| 171 | try { |
| 172 | if ($input.hasClass('ui-autocomplete-input')) { |
| 173 | $input.autocomplete('destroy'); |
| 174 | } |
| 175 | } catch (e) { |
| 176 | // If destroy fails, just remove the class |
| 177 | $input.removeClass('ui-autocomplete-input'); |
| 178 | } |
| 179 | }; |
| 180 | } |
| 181 | }, [showSearch, selectedPostType]); |
| 182 | var selectPost = function selectPost(post) { |
| 183 | setAttributes({ |
| 184 | selectedPostId: post.id, |
| 185 | selectedPostTitle: post.title, |
| 186 | selectedPostType: post.type |
| 187 | }); |
| 188 | setShowSearch(false); |
| 189 | setSearchTerm(''); |
| 190 | setSearchError(''); |
| 191 | }; |
| 192 | var clearSelection = function clearSelection() { |
| 193 | setAttributes({ |
| 194 | selectedPostId: 0, |
| 195 | selectedPostTitle: '', |
| 196 | selectedPostContent: '' |
| 197 | }); |
| 198 | setShowSearch(false); |
| 199 | setSearchTerm(''); |
| 200 | setSearchError(''); |
| 201 | }; |
| 202 | var blockProps = useBlockProps({ |
| 203 | className: "frbl-insert-post-block ".concat(className) |
| 204 | }); |
| 205 | return /*#__PURE__*/React.createElement(Fragment, null, /*#__PURE__*/React.createElement("div", blockProps, selectedPostId ? /*#__PURE__*/React.createElement("div", { |
| 206 | className: "frbl-insert-post-preview" |
| 207 | }, /*#__PURE__*/React.createElement("h2", { |
| 208 | className: "frbl-insert-post-title" |
| 209 | }, selectedPostTitle), /*#__PURE__*/React.createElement("div", { |
| 210 | className: "frbl-insert-post-content" |
| 211 | }, selectedPostContent || __('Content will be loaded on the frontend', 'frontblocks')), /*#__PURE__*/React.createElement("div", { |
| 212 | className: "frbl-insert-post-actions" |
| 213 | }, /*#__PURE__*/React.createElement(Button, { |
| 214 | isSecondary: true, |
| 215 | onClick: function onClick() { |
| 216 | return setShowSearch(true); |
| 217 | } |
| 218 | }, __('Change Post', 'frontblocks')), /*#__PURE__*/React.createElement(Button, { |
| 219 | isDestructive: true, |
| 220 | onClick: clearSelection |
| 221 | }, __('Clear Selection', 'frontblocks')))) : /*#__PURE__*/React.createElement("div", { |
| 222 | className: "frbl-insert-post-empty" |
| 223 | }, /*#__PURE__*/React.createElement("p", null, __('No post selected. Use the sidebar to search and select a post.', 'frontblocks')), /*#__PURE__*/React.createElement(Button, { |
| 224 | isPrimary: true, |
| 225 | onClick: function onClick() { |
| 226 | return setShowSearch(true); |
| 227 | } |
| 228 | }, __('Select Post', 'frontblocks')))), /*#__PURE__*/React.createElement(InspectorControls, null, /*#__PURE__*/React.createElement(PanelBody, { |
| 229 | title: __('Insert Post Settings', 'frontblocks'), |
| 230 | initialOpen: true |
| 231 | }, (!selectedPostId || showSearch) && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(SelectControl, { |
| 232 | label: __('Post Type', 'frontblocks'), |
| 233 | value: selectedPostType, |
| 234 | options: postTypes, |
| 235 | onChange: function onChange(value) { |
| 236 | return setAttributes({ |
| 237 | selectedPostType: value |
| 238 | }); |
| 239 | } |
| 240 | }), /*#__PURE__*/React.createElement(TextControl, { |
| 241 | ref: searchInputRef, |
| 242 | label: __('Search Posts', 'frontblocks'), |
| 243 | value: searchTerm, |
| 244 | onChange: setSearchTerm, |
| 245 | placeholder: __('Start typing to search posts...', 'frontblocks'), |
| 246 | help: __('Type at least 2 characters to search', 'frontblocks') |
| 247 | }), isSearching && /*#__PURE__*/React.createElement("div", { |
| 248 | style: { |
| 249 | marginTop: '10px' |
| 250 | } |
| 251 | }, /*#__PURE__*/React.createElement(Spinner, null), /*#__PURE__*/React.createElement("span", { |
| 252 | style: { |
| 253 | marginLeft: '8px' |
| 254 | } |
| 255 | }, __('Searching...', 'frontblocks'))), searchError && /*#__PURE__*/React.createElement(Notice, { |
| 256 | status: "error", |
| 257 | isDismissible: false |
| 258 | }, searchError)), selectedPostId && !showSearch && /*#__PURE__*/React.createElement("div", { |
| 259 | className: "frbl-selected-post-info" |
| 260 | }, /*#__PURE__*/React.createElement("h4", null, __('Selected Post', 'frontblocks')), /*#__PURE__*/React.createElement(Card, null, /*#__PURE__*/React.createElement(CardBody, null, /*#__PURE__*/React.createElement("p", null, /*#__PURE__*/React.createElement("strong", null, __('Title:', 'frontblocks')), " ", selectedPostTitle), /*#__PURE__*/React.createElement("p", null, /*#__PURE__*/React.createElement("strong", null, __('Type:', 'frontblocks')), " ", selectedPostType), /*#__PURE__*/React.createElement("p", null, /*#__PURE__*/React.createElement("strong", null, __('ID:', 'frontblocks')), " ", selectedPostId))), /*#__PURE__*/React.createElement(Button, { |
| 261 | isSecondary: true, |
| 262 | onClick: function onClick() { |
| 263 | return setShowSearch(true); |
| 264 | }, |
| 265 | style: { |
| 266 | marginTop: '10px' |
| 267 | } |
| 268 | }, __('Change Post', 'frontblocks')))))); |
| 269 | } |
| 270 | |
| 271 | // Register the custom block |
| 272 | var registerBlockType = wp.blocks.registerBlockType; |
| 273 | registerBlockType('frontblocks/insert-post', { |
| 274 | title: __('FrontBlocks - Insert Post', 'frontblocks'), |
| 275 | description: __('Display content from another post or page', 'frontblocks'), |
| 276 | category: 'generateblocks', |
| 277 | icon: 'admin-post', |
| 278 | keywords: [__('post', 'frontblocks'), __('content', 'frontblocks'), __('insert', 'frontblocks'), __('display', 'frontblocks')], |
| 279 | supports: { |
| 280 | html: false, |
| 281 | align: ['wide', 'full'] |
| 282 | }, |
| 283 | attributes: { |
| 284 | selectedPostId: { |
| 285 | type: 'number', |
| 286 | default: 0 |
| 287 | }, |
| 288 | selectedPostType: { |
| 289 | type: 'string', |
| 290 | default: 'post' |
| 291 | }, |
| 292 | selectedPostTitle: { |
| 293 | type: 'string', |
| 294 | default: '' |
| 295 | }, |
| 296 | selectedPostContent: { |
| 297 | type: 'string', |
| 298 | default: '' |
| 299 | }, |
| 300 | className: { |
| 301 | type: 'string', |
| 302 | default: '' |
| 303 | } |
| 304 | }, |
| 305 | edit: InsertPostBlock, |
| 306 | save: function save() { |
| 307 | return null; |
| 308 | } // Using PHP render callback |
| 309 | }); |
| 310 | |
| 311 | // Add custom panel to existing GenerateBlocks Grid block |
| 312 | function addInsertPostPanel(BlockEdit) { |
| 313 | return function (props) { |
| 314 | if (props.name !== 'generateblocks/grid') { |
| 315 | return /*#__PURE__*/React.createElement(BlockEdit, props); |
| 316 | } |
| 317 | var _props$attributes$frb = props.attributes.frblInsertPostEnabled, |
| 318 | frblInsertPostEnabled = _props$attributes$frb === void 0 ? false : _props$attributes$frb; |
| 319 | return /*#__PURE__*/React.createElement(Fragment, null, /*#__PURE__*/React.createElement(BlockEdit, props), /*#__PURE__*/React.createElement(InspectorControls, null, /*#__PURE__*/React.createElement(PanelBody, { |
| 320 | title: __('FrontBlocks - Insert Post', 'frontblocks'), |
| 321 | initialOpen: false |
| 322 | }, /*#__PURE__*/React.createElement(ToggleControl, { |
| 323 | label: __('Enable Insert Post Grid', 'frontblocks'), |
| 324 | checked: frblInsertPostEnabled, |
| 325 | onChange: function onChange(value) { |
| 326 | return props.setAttributes({ |
| 327 | frblInsertPostEnabled: value |
| 328 | }); |
| 329 | }, |
| 330 | help: __('Enable insert post functionality for this grid', 'frontblocks') |
| 331 | })))); |
| 332 | }; |
| 333 | } |
| 334 | addFilter('editor.BlockEdit', 'frontblocks/insert-post-grid-panel', addInsertPostPanel); |
| 335 |