PluginProbe ʕ •ᴥ•ʔ
FrontBlocks for Gutenberg/GeneratePress / trunk
FrontBlocks for Gutenberg/GeneratePress vtrunk
trunk 0.2.0 0.2.1 0.2.2 0.2.3 0.2.4 0.2.5 1.0.0 1.0.1 1.0.2 1.0.3 1.0.4 1.1.0 1.2.0 1.2.1 1.3.0 1.3.1 1.3.2 1.3.3 1.3.4 1.3.5 1.3.6 ci-artifacts
frontblocks / assets / reading-time / frontblocks-reading-time.js
frontblocks / assets / reading-time Last commit date
frontblocks-reading-time-option.jsx 1 month ago frontblocks-reading-time.css 8 months ago frontblocks-reading-time.js 1 month ago
frontblocks-reading-time.js
315 lines
1 "use strict";
2
3 function _slicedToArray(r, e) { return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest(); }
4 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."); }
5 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; } }
6 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; }
7 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; } }
8 function _arrayWithHoles(r) { if (Array.isArray(r)) return r; }
9 var registerBlockType = wp.blocks.registerBlockType;
10 var _wp$element = wp.element,
11 Fragment = _wp$element.Fragment,
12 useState = _wp$element.useState,
13 useEffect = _wp$element.useEffect;
14 var _wp$blockEditor = wp.blockEditor,
15 InspectorControls = _wp$blockEditor.InspectorControls,
16 useBlockProps = _wp$blockEditor.useBlockProps,
17 ColorPalette = _wp$blockEditor.ColorPalette;
18 var _wp$components = wp.components,
19 PanelBody = _wp$components.PanelBody,
20 ToggleControl = _wp$components.ToggleControl,
21 TextControl = _wp$components.TextControl,
22 RangeControl = _wp$components.RangeControl,
23 SelectControl = _wp$components.SelectControl;
24 var __ = wp.i18n.__;
25 var useSelect = wp.data.useSelect;
26
27 /**
28 * Calculate reading time for content.
29 *
30 * @param {string} content - Post content.
31 * @return {number} Reading time in minutes.
32 */
33 function calculateReadingTime(content) {
34 // Ensure content is a string.
35 if (!content || typeof content !== 'string') {
36 return 5;
37 }
38
39 // Strip HTML tags.
40 var textContent = content.replace(/<[^>]*>/g, '');
41
42 // Count words.
43 var wordCount = textContent.split(/\s+/).filter(function (word) {
44 return word.length > 0;
45 }).length;
46
47 // Average reading speed: 225 words per minute.
48 var readingTime = Math.ceil(wordCount / 225);
49
50 // Minimum 1 minute.
51 return Math.max(1, readingTime);
52 }
53
54 /**
55 * Edit component for Reading Time block.
56 *
57 * @param {Object} props - Block properties.
58 * @return {JSX.Element} Block edit component.
59 */
60 function ReadingTimeEdit(props) {
61 var attributes = props.attributes,
62 setAttributes = props.setAttributes;
63 var postId = attributes.postId,
64 showIcon = attributes.showIcon,
65 prefix = attributes.prefix,
66 suffix = attributes.suffix,
67 textColor = attributes.textColor,
68 backgroundColor = attributes.backgroundColor,
69 fontSize = attributes.fontSize,
70 iconColor = attributes.iconColor,
71 alignment = attributes.alignment,
72 padding = attributes.padding,
73 borderRadius = attributes.borderRadius;
74 var _useState = useState(5),
75 _useState2 = _slicedToArray(_useState, 2),
76 readingTime = _useState2[0],
77 setReadingTime = _useState2[1];
78 var blockProps = useBlockProps();
79
80 // Get current post content and calculate reading time.
81 useEffect(function () {
82 try {
83 var _wp$data$select, _wp$data$select$getEd;
84 // Try to get the post content from the editor.
85 var postContent = (_wp$data$select = wp.data.select('core/editor')) === null || _wp$data$select === void 0 || (_wp$data$select$getEd = _wp$data$select.getEditedPostContent) === null || _wp$data$select$getEd === void 0 ? void 0 : _wp$data$select$getEd.call(_wp$data$select);
86 if (postContent && typeof postContent === 'string') {
87 var calculatedTime = calculateReadingTime(postContent);
88 setReadingTime(calculatedTime);
89 } else {
90 setReadingTime(5);
91 }
92 } catch (error) {
93 // If there's any error, just use default value.
94 setReadingTime(5);
95 }
96 }, [postId]);
97 var styleVars = {
98 '--frbl-text-color': textColor,
99 '--frbl-bg-color': backgroundColor,
100 '--frbl-font-size': "".concat(fontSize, "px"),
101 '--frbl-icon-color': iconColor,
102 '--frbl-padding': "".concat(padding, "px"),
103 '--frbl-border-radius': "".concat(borderRadius, "px")
104 };
105 var iconSvg = /*#__PURE__*/React.createElement("svg", {
106 xmlns: "http://www.w3.org/2000/svg",
107 width: "1em",
108 height: "1em",
109 viewBox: "0 0 24 24",
110 fill: "none",
111 stroke: "currentColor",
112 strokeWidth: "2",
113 strokeLinecap: "round",
114 strokeLinejoin: "round"
115 }, /*#__PURE__*/React.createElement("circle", {
116 cx: "12",
117 cy: "12",
118 r: "10"
119 }), /*#__PURE__*/React.createElement("polyline", {
120 points: "12 6 12 12 16 14"
121 }));
122 var wrapperClass = "frbl-reading-time-wrapper align-".concat(alignment);
123 return /*#__PURE__*/React.createElement(Fragment, null, /*#__PURE__*/React.createElement(InspectorControls, null, /*#__PURE__*/React.createElement(PanelBody, {
124 title: __('General Settings', 'frontblocks'),
125 initialOpen: true
126 }, /*#__PURE__*/React.createElement(ToggleControl, {
127 label: __('Show Icon', 'frontblocks'),
128 checked: showIcon,
129 onChange: function onChange(value) {
130 return setAttributes({
131 showIcon: value
132 });
133 }
134 }), /*#__PURE__*/React.createElement(TextControl, {
135 label: __('Prefix', 'frontblocks'),
136 value: prefix,
137 onChange: function onChange(value) {
138 return setAttributes({
139 prefix: value
140 });
141 },
142 help: __('Text to display before the reading time number. Example: "This blog takes"', 'frontblocks'),
143 placeholder: __('Example: This blog takes', 'frontblocks')
144 }), /*#__PURE__*/React.createElement(TextControl, {
145 label: __('Suffix', 'frontblocks'),
146 value: suffix,
147 onChange: function onChange(value) {
148 return setAttributes({
149 suffix: value
150 });
151 },
152 help: __('Text to display after the reading time number. Example: "minutes to read"', 'frontblocks'),
153 placeholder: __('Example: minutes to read', 'frontblocks')
154 }), /*#__PURE__*/React.createElement(SelectControl, {
155 label: __('Alignment', 'frontblocks'),
156 value: alignment,
157 options: [{
158 label: __('Left', 'frontblocks'),
159 value: 'left'
160 }, {
161 label: __('Center', 'frontblocks'),
162 value: 'center'
163 }, {
164 label: __('Right', 'frontblocks'),
165 value: 'right'
166 }],
167 onChange: function onChange(value) {
168 return setAttributes({
169 alignment: value
170 });
171 }
172 })), /*#__PURE__*/React.createElement(PanelBody, {
173 title: __('Style Settings', 'frontblocks'),
174 initialOpen: false
175 }, /*#__PURE__*/React.createElement(RangeControl, {
176 label: __('Font Size', 'frontblocks'),
177 value: fontSize,
178 onChange: function onChange(value) {
179 return setAttributes({
180 fontSize: value
181 });
182 },
183 min: 10,
184 max: 48
185 }), /*#__PURE__*/React.createElement("p", {
186 style: {
187 marginBottom: '8px',
188 fontWeight: '500'
189 }
190 }, __('Text Color', 'frontblocks')), /*#__PURE__*/React.createElement(ColorPalette, {
191 value: textColor,
192 onChange: function onChange(value) {
193 return setAttributes({
194 textColor: value || 'inherit'
195 });
196 }
197 }), /*#__PURE__*/React.createElement("p", {
198 style: {
199 marginBottom: '8px',
200 fontWeight: '500'
201 }
202 }, __('Background Color', 'frontblocks')), /*#__PURE__*/React.createElement(ColorPalette, {
203 value: backgroundColor,
204 onChange: function onChange(value) {
205 return setAttributes({
206 backgroundColor: value || 'transparent'
207 });
208 }
209 }), /*#__PURE__*/React.createElement("p", {
210 style: {
211 marginBottom: '8px',
212 fontWeight: '500'
213 }
214 }, __('Icon Color', 'frontblocks')), /*#__PURE__*/React.createElement(ColorPalette, {
215 value: iconColor,
216 onChange: function onChange(value) {
217 return setAttributes({
218 iconColor: value || 'currentColor'
219 });
220 }
221 }), /*#__PURE__*/React.createElement(RangeControl, {
222 label: __('Padding', 'frontblocks'),
223 value: padding,
224 onChange: function onChange(value) {
225 return setAttributes({
226 padding: value
227 });
228 },
229 min: 0,
230 max: 50
231 }), /*#__PURE__*/React.createElement(RangeControl, {
232 label: __('Border Radius', 'frontblocks'),
233 value: borderRadius,
234 onChange: function onChange(value) {
235 return setAttributes({
236 borderRadius: value
237 });
238 },
239 min: 0,
240 max: 50
241 }))), /*#__PURE__*/React.createElement("div", blockProps, /*#__PURE__*/React.createElement("div", {
242 className: wrapperClass
243 }, /*#__PURE__*/React.createElement("div", {
244 className: "frbl-reading-time",
245 style: styleVars
246 }, showIcon && /*#__PURE__*/React.createElement("span", {
247 className: "frbl-reading-time-icon"
248 }, iconSvg), /*#__PURE__*/React.createElement("span", {
249 className: "frbl-reading-time-text"
250 }, prefix && "".concat(prefix, " "), readingTime, suffix && " ".concat(suffix))))));
251 }
252
253 // Register the block.
254 registerBlockType('frontblocks/reading-time', {
255 title: __('FrontBlocks - Reading Time', 'frontblocks'),
256 description: __('Display estimated reading time for the current post or page.', 'frontblocks'),
257 category: 'common',
258 icon: 'clock',
259 keywords: [__('reading', 'frontblocks'), __('time', 'frontblocks'), __('duration', 'frontblocks'), __('blog', 'frontblocks')],
260 attributes: {
261 postId: {
262 type: 'number',
263 default: 0
264 },
265 showIcon: {
266 type: 'boolean',
267 default: true
268 },
269 prefix: {
270 type: 'string',
271 default: ''
272 },
273 suffix: {
274 type: 'string',
275 default: 'min'
276 },
277 className: {
278 type: 'string',
279 default: ''
280 },
281 textColor: {
282 type: 'string',
283 default: 'inherit'
284 },
285 backgroundColor: {
286 type: 'string',
287 default: 'transparent'
288 },
289 fontSize: {
290 type: 'number',
291 default: 16
292 },
293 iconColor: {
294 type: 'string',
295 default: 'currentColor'
296 },
297 alignment: {
298 type: 'string',
299 default: 'left'
300 },
301 padding: {
302 type: 'number',
303 default: 10
304 },
305 borderRadius: {
306 type: 'number',
307 default: 5
308 }
309 },
310 edit: ReadingTimeEdit,
311 save: function save() {
312 return null; // Dynamic block, render on server side.
313 }
314 });
315