PluginProbe
Smart Post – Post Grid, Post Carousel, Post Slider Gutenberg Blocks for Blog & News / 4.0.8
Smart Post – Post Grid, Post Carousel, Post Slider Gutenberg Blocks for Blog & News v4.0.8
4.0.8 4.0.7 4.0.6 4.0.5 4.0.4 4.0.3 4.0.2 4.0.1 2.3.5 2.3.6 2.4.0 2.4.1 2.4.10 2.4.11 2.4.12 2.4.13 2.4.14 2.4.15 2.4.16 2.4.17 2.4.18 2.4.19 2.4.2 2.4.20 2.4.21 All 88 releases
post-carousel / src / saved-template-sidebar / index.js

index.js in Smart Post – Post Grid, Post Carousel, Post Slider Gutenberg Blocks for Blog & News 4.0.8, at src/saved-template-sidebar/index.js

165 lines 4.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 import { __ } from "@wordpress/i18n";
2 import { registerPlugin } from "@wordpress/plugins";
3 import { useSelect } from "@wordpress/data";
4 import { createPortal, useEffect, useState } from "@wordpress/element";
5 import "./editor.scss";
6
7 const CopyIcon = () => (
8 <svg
9 width="18"
10 height="18"
11 viewBox="0 0 18 18"
12 fill="none"
13 xmlns="http://www.w3.org/2000/svg"
14 aria-hidden="true"
15 focusable="false"
16 >
17 <path
18 d="M6 6V3.75A.75.75 0 0 1 6.75 3h7.5a.75.75 0 0 1 .75.75v7.5a.75.75 0 0 1-.75.75H12M3.75 6.75h7.5a.75.75 0 0 1 .75.75v7.5a.75.75 0 0 1-.75.75h-7.5a.75.75 0 0 1-.75-.75v-7.5a.75.75 0 0 1 .75-.75Z"
19 stroke="currentColor"
20 strokeWidth="1.2"
21 strokeLinecap="round"
22 strokeLinejoin="round"
23 />
24 </svg>
25 );
26
27 const PORTAL_CLASSNAME = "spspc-shortcode-portal";
28
29 const copyText = async (text) => {
30 // First try Clipboard API
31 if (navigator.clipboard && navigator.clipboard.writeText) {
32 try {
33 await navigator.clipboard.writeText(text);
34 return true;
35 } catch (e) {
36 console.log("Clipboard API failed, using fallback.", e);
37 }
38 }
39
40 // Fallback method: hidden textarea + execCommand
41 try {
42 const textarea = document.createElement("textarea");
43 textarea.value = text;
44
45 // Hide from screen
46 textarea.style.position = "fixed";
47 textarea.style.opacity = "0";
48 textarea.style.pointerEvents = "none";
49
50 document.body.appendChild(textarea);
51 textarea.select();
52
53 const success = document.execCommand("copy");
54 document.body.removeChild(textarea);
55
56 return success;
57 } catch (err) {
58 console.log("Fallback copy failed:", err);
59 return false;
60 }
61 };
62
63 const SavedTemplateSidebar = () => {
64 const { postType, postId } = useSelect(
65 (select) => ({
66 postType: select("core/editor")?.getCurrentPostType?.() || null,
67 postId: select("core/editor")?.getCurrentPostId?.() || null,
68 }),
69 []
70 );
71
72 const [copied, setCopied] = useState(false);
73 const [portalTarget, setPortalTarget] = useState(null);
74
75 useEffect(() => {
76 if (postType !== "sp_post_template") {
77 setPortalTarget(null);
78 return undefined;
79 }
80
81 let container = null;
82
83 const ensureContainer = () => {
84 const fill = document.querySelector(".interface-complementary-area__fill");
85 if (!fill) {
86 return;
87 }
88
89 let existing = fill.querySelector(`:scope > .${PORTAL_CLASSNAME}`);
90 if (!existing) {
91 existing = document.createElement("div");
92 existing.className = PORTAL_CLASSNAME;
93 fill.insertBefore(existing, fill.firstChild);
94 } else if (fill.firstElementChild !== existing) {
95 fill.insertBefore(existing, fill.firstChild);
96 }
97
98 if (existing !== container) {
99 container = existing;
100 setPortalTarget(existing);
101 }
102 };
103
104 ensureContainer();
105
106 const observer = new window.MutationObserver(ensureContainer);
107 observer.observe(document.body, { childList: true, subtree: true });
108
109 return () => {
110 observer.disconnect();
111 if (container && container.parentNode) {
112 container.parentNode.removeChild(container);
113 }
114 setPortalTarget(null);
115 };
116 }, [postType]);
117
118 if (postType !== "sp_post_template" || !portalTarget) {
119 return null;
120 }
121
122 const idForShortcode = postId || 0;
123 const shortcode = `[smart_post id="${idForShortcode}"]`;
124 const savedTemplatesUrl =
125 (window.sp_smart_post_block_localize && window.sp_smart_post_block_localize.savedTemplatesUrl) ||
126 (window.sp_pcp_block_settings && window.sp_pcp_block_settings.savedTemplatesUrl) ||
127 "";
128
129 const handleCopy = async () => {
130 const ok = await copyText(shortcode);
131 if (!ok) {
132 return;
133 }
134 setCopied(true);
135 window.setTimeout(() => setCopied(false), 1500);
136 };
137
138 return createPortal(
139 <div className="spspc-shortcode-panel">
140 <p className="spspc-shortcode-panel__intro">
141 {__("You can use this shortcode anywhere and manage it from", "post-carousel")}{" "}
142 <a className="spspc-shortcode-panel__link" href={savedTemplatesUrl} rel="noopener noreferrer">
143 {__("Saved Templates.", "post-carousel")}
144 </a>
145 </p>
146 <button
147 type="button"
148 className="spspc-shortcode-panel__chip"
149 onClick={handleCopy}
150 aria-label={copied ? __("Shortcode copied", "post-carousel") : __("Copy shortcode", "post-carousel")}
151 >
152 <span className="spspc-shortcode-panel__code">{shortcode}</span>
153 <span className="spspc-shortcode-panel__copy">
154 {copied ? <span className="spspc-shortcode-panel__copy-text">Copied!</span> : <CopyIcon />}
155 </span>
156 </button>
157 </div>,
158 portalTarget
159 );
160 };
161
162 registerPlugin("spspc-saved-template-sidebar", {
163 render: SavedTemplateSidebar,
164 });
165