PluginProbe
aBlocks – Gutenberg Blocks, User Dashboard Builder, Popup Builder, Form Builder & Animation Builder / 2.14.0
aBlocks – Gutenberg Blocks, User Dashboard Builder, Popup Builder, Form Builder & Animation Builder v2.14.0
2.14.0 2.13.0 2.13.1 2.12.0 2.11.1 2.11.0 2.10.0 2.9.0 2.7.4 2.7.5 2.7.6 2.7.7 2.8.0 2.8.1 2.9.1 trunk 1.0 1.0-beta1 1.0-beta2 1.0-beta3 1.0.1 1.0.2 1.0.3 1.1.0 1.1.1 All 81 releases
← All changes | includes/blocks/table-of-content/block.php +405 -0 2.7.6 → 2.14.0 View file →
@@ -1,0 +1,405 @@
1 +<?php
2 +namespace ABlocks\Blocks\TableOfContent;
3 +
4 +if ( ! defined( 'ABSPATH' ) ) {
5 + exit;
6 +}
7 +
8 +use ABlocks\Classes\BlockBaseAbstract;
9 +use ABlocks\Classes\CssGenerator;
10 +use ABlocks\Classes\CssGeneratorV2;
11 +use ABlocks\Helper;
12 +use ABlocks\Controls\Typography;
13 +use ABlocks\Controls\Background;
14 +use ABlocks\Controls\Border;
15 +use ABlocks\Controls\Dimensions;
16 +use ABlocks\Controls\Alignment;
17 +use ABlocks\Controls\Range;
18 +use ABlocks\Controls\Color;
19 +use ABlocks\Controls\BoxShadow;
20 +
21 +class Block extends BlockBaseAbstract {
22 + protected $block_name = 'table-of-content';
23 +
24 +
25 + public function build_css_v1( $attributes ) {
26 + $css_generator = new CssGenerator( $attributes );
27 +
28 + $css_generator->add_class_styles(
29 + '{{WRAPPER}} .ablocks-toc__header-title',
30 + $this->get_toc_title_css( $attributes ),
31 + $this->get_toc_title_css( $attributes, 'Tablet' ),
32 + $this->get_toc_title_css( $attributes, 'Mobile' ),
33 + $css_generator->custom_device_map( function ( $device ) use ( $attributes ) { return $this->get_toc_title_css( $attributes, $device ); } )
34 + );
35 + $css_generator->add_class_styles(
36 + '{{WRAPPER}} .ablocks-toc__header',
37 + $this->get_toc_header_css( $attributes ),
38 + $this->get_toc_header_css( $attributes, 'Tablet' ),
39 + $this->get_toc_header_css( $attributes, 'Mobile' ),
40 + $css_generator->custom_device_map( function ( $device ) use ( $attributes ) { return $this->get_toc_header_css( $attributes, $device ); } )
41 + );
42 + $css_generator->add_class_styles(
43 + '{{WRAPPER}} .ablocks-toc-body',
44 + $this->get_toc_body_css( $attributes ),
45 + $this->get_toc_body_css( $attributes, 'Tablet' ),
46 + $this->get_toc_body_css( $attributes, 'Mobile' ),
47 + $css_generator->custom_device_map( function ( $device ) use ( $attributes ) { return $this->get_toc_body_css( $attributes, $device ); } )
48 + );
49 + $css_generator->add_class_styles(
50 + '{{WRAPPER}} .ablocks-toc__header-toggle-icon .ablocks-toc__show',
51 + $this->get_toc_header_icon_css( $attributes ),
52 + $this->get_toc_header_icon_css( $attributes, 'Tablet' ),
53 + $this->get_toc_header_icon_css( $attributes, 'Mobile' ),
54 + $css_generator->custom_device_map( function ( $device ) use ( $attributes ) { return $this->get_toc_header_icon_css( $attributes, $device ); } )
55 + );
56 + $css_generator->add_class_styles(
57 + '{{WRAPPER}} .ablocks-toc__header-toggle-icon .ablocks-toc__show:hover',
58 + $this->get_toc_header_icon_hover_css( $attributes ),
59 + $this->get_toc_header_icon_hover_css( $attributes, 'Tablet' ),
60 + $this->get_toc_header_icon_hover_css( $attributes, 'Mobile' ),
61 + $css_generator->custom_device_map( function ( $device ) use ( $attributes ) { return $this->get_toc_header_icon_hover_css( $attributes, $device ); } )
62 + );
63 + $css_generator->add_class_styles(
64 + '{{WRAPPER}} .ablocks-block-container .ablocks-toc-list,
65 + {{WRAPPER}} .ablocks-block-container .ablocks-toc-list li a',
66 + $this->get_list_item_css( $attributes ),
67 + $this->get_list_item_css( $attributes, 'Tablet' ),
68 + $this->get_list_item_css( $attributes, 'Mobile' ),
69 + $css_generator->custom_device_map( function ( $device ) use ( $attributes ) { return $this->get_list_item_css( $attributes, $device ); } )
70 + );
71 +
72 + return $css_generator->generate_css();
73 + }
74 + public function build_css_v2( $attributes ) {
75 + $css_generator = new CssGeneratorV2( $attributes, $this->block_name );
76 +
77 + $css_generator->add_class_styles(
78 + '{{WRAPPER}} .ablocks-toc__header-title',
79 + $this->get_toc_title_css( $attributes ),
80 + $this->get_toc_title_css( $attributes, 'Tablet' ),
81 + $this->get_toc_title_css( $attributes, 'Mobile' ),
82 + $css_generator->custom_device_map( function ( $device ) use ( $attributes ) { return $this->get_toc_title_css( $attributes, $device ); } )
83 + );
84 + $css_generator->add_class_styles(
85 + '{{WRAPPER}} .ablocks-toc__header',
86 + $this->get_toc_header_css( $attributes ),
87 + $this->get_toc_header_css( $attributes, 'Tablet' ),
88 + $this->get_toc_header_css( $attributes, 'Mobile' ),
89 + $css_generator->custom_device_map( function ( $device ) use ( $attributes ) { return $this->get_toc_header_css( $attributes, $device ); } )
90 + );
91 + $css_generator->add_class_styles(
92 + '{{WRAPPER}} .ablocks-toc-body',
93 + $this->get_toc_body_css( $attributes ),
94 + $this->get_toc_body_css( $attributes, 'Tablet' ),
95 + $this->get_toc_body_css( $attributes, 'Mobile' ),
96 + $css_generator->custom_device_map( function ( $device ) use ( $attributes ) { return $this->get_toc_body_css( $attributes, $device ); } )
97 + );
98 + $css_generator->add_class_styles(
99 + '{{WRAPPER}} .ablocks-toc__header-toggle-icon .ablocks-toc__show',
100 + $this->get_toc_header_icon_css( $attributes ),
101 + $this->get_toc_header_icon_css( $attributes, 'Tablet' ),
102 + $this->get_toc_header_icon_css( $attributes, 'Mobile' ),
103 + $css_generator->custom_device_map( function ( $device ) use ( $attributes ) { return $this->get_toc_header_icon_css( $attributes, $device ); } )
104 + );
105 + $css_generator->add_class_styles(
106 + '{{WRAPPER}} .ablocks-toc__header-toggle-icon .ablocks-toc__show:hover',
107 + $this->get_toc_header_icon_hover_css( $attributes ),
108 + $this->get_toc_header_icon_hover_css( $attributes, 'Tablet' ),
109 + $this->get_toc_header_icon_hover_css( $attributes, 'Mobile' ),
110 + $css_generator->custom_device_map( function ( $device ) use ( $attributes ) { return $this->get_toc_header_icon_hover_css( $attributes, $device ); } )
111 + );
112 + $css_generator->add_class_styles(
113 + '{{WRAPPER}} .ablocks-toc-body .ablocks-toc-list',
114 + $this->get_marker_list_style_css( $attributes ),
115 + $this->get_marker_list_style_css( $attributes, 'Tablet' ),
116 + $this->get_marker_list_style_css( $attributes, 'Mobile' ),
117 + $css_generator->custom_device_map( function ( $device ) use ( $attributes ) { return $this->get_marker_list_style_css( $attributes, $device ); } )
118 + );
119 + $css_generator->add_class_styles(
120 + '{{WRAPPER}} .ablocks-toc-list,
121 + {{WRAPPER}} .ablocks-toc-list li a',
122 + $this->get_list_item_css( $attributes ),
123 + $this->get_list_item_css( $attributes, 'Tablet' ),
124 + $this->get_list_item_css( $attributes, 'Mobile' ),
125 + $css_generator->custom_device_map( function ( $device ) use ( $attributes ) { return $this->get_list_item_css( $attributes, $device ); } )
126 + );
127 + $css_generator->add_class_styles(
128 + '{{WRAPPER}} .ablocks-toc-body .ablocks-toc-list li a',
129 + $this->get_list_item_gap_css( $attributes ),
130 + $this->get_list_item_gap_css( $attributes, 'Tablet' ),
131 + $this->get_list_item_gap_css( $attributes, 'Mobile' ),
132 + $css_generator->custom_device_map( function ( $device ) use ( $attributes ) { return $this->get_list_item_gap_css( $attributes, $device ); } )
133 + );
134 + $css_generator->add_class_styles(
135 + '{{WRAPPER}} a.ablocks-toc-item-link.active',
136 + $this->get_active_list_item_css( $attributes ),
137 + $this->get_active_list_item_css( $attributes, 'Tablet' ),
138 + $this->get_active_list_item_css( $attributes, 'Mobile' ),
139 + $css_generator->custom_device_map( function ( $device ) use ( $attributes ) { return $this->get_active_list_item_css( $attributes, $device ); } )
140 + );
141 +
142 + return $css_generator->generate_css();
143 + }
144 +
145 + public function build_css( $attributes ) {
146 + if ( isset( $attributes['blockVersion'] ) && (int) $attributes['blockVersion'] === 2 ) {
147 + return $this->build_css_v2( $attributes );
148 + }
149 + return $this->build_css_v1( $attributes );
150 + }
151 +
152 +
153 + public function get_toc_title_css( $attributes, $device = '' ) {
154 + $typographyValueGlobal = ! empty( $attributes['titleTypographyGlobal'] ) ? $attributes['titleTypographyGlobal'] : array();
155 + $toc_title_typography_css = ! empty( $attributes['titleTypography'] ) ? Typography::get_css( $attributes['titleTypography'], '', $device, $typographyValueGlobal ) : array();
156 + return array_merge( $toc_title_typography_css,
157 + [ 'color' => Color::get_css( isset( $attributes['titleColor'] ) ? $attributes['titleColor'] : '' ) ], );
158 + }
159 + public function get_toc_header_css( $attributes, $device = '' ) {
160 + $headerorder = ! empty( $attributes['headerBorder'] ) ? Border::get_css( $attributes['headerBorder'], '', $device ) : array();
161 + $header_padding_css = ! empty( $attributes['header_padding'] ) ? Dimensions::get_css( $attributes['header_padding'], 'padding', $device ) : array();
162 + return array_merge(
163 + [ 'background' => Color::get_css( isset( $attributes['headerBG'] ) ? $attributes['headerBG'] : '' ) ],
164 + $headerorder, $header_padding_css );
165 + }
166 + public function get_toc_body_css( $attributes, $device = '' ) {
167 + $listing_padding_css = ! empty( $attributes['list_padding'] ) ? Dimensions::get_css( $attributes['list_padding'], 'padding', $device ) : array();
168 + return array_merge(
169 + [ 'background' => Color::get_css( isset( $attributes['bodyBG'] ) ? $attributes['bodyBG'] : '' ) ],
170 + $listing_padding_css, );
171 + }
172 + public function get_toc_header_icon_css( $attributes, $device = '' ) {
173 + $css = array_merge(
174 + [ 'fill' => Color::get_css( isset( $attributes['iconColor'] ) ? $attributes['iconColor'] : '' ) ],
175 + Range::get_css([
176 + 'attributeValue' => $attributes['iconSize'] ?? null,
177 + 'isResponsive' => false,
178 + 'defaultValue' => 20,
179 + 'unitDefaultValue' => 'px',
180 + 'property' => 'font-size',
181 + 'device' => $device,
182 + ]),
183 + Dimensions::get_css( $attributes['icon_padding'] ?? [], 'padding', $device ),
184 + Border::get_css( $attributes['iconBorder'] ?? [], '', $device ),
185 + BoxShadow::get_css( $attributes['iconBoxShadow'] ?? [], $device ),
186 + );
187 + return $css;
188 + }
189 +
190 + public function get_toc_header_icon_hover_css( $attributes, $device = '' ) {
191 + $css = array_merge(
192 + Border::get_hover_css( $attributes['iconBorder'], $device ),
193 + BoxShadow::get_hover_css( $attributes['iconBoxShadow'], $device )
194 + );
195 + return $css;
196 + }
197 +
198 + public function get_list_item_css( $attributes, $device = '' ) {
199 + $typographyValueGlobal = ! empty( $attributes['contentTypographyGlobal'] ) ? $attributes['contentTypographyGlobal'] : array();
200 + $contentTypography = ! empty( $attributes['contentTypography'] ) ? Typography::get_css( $attributes['contentTypography'], '', $device, $typographyValueGlobal ) : array();
201 + return array_merge(
202 + [ 'color' => Color::get_css( isset( $attributes['itemColor'] ) ? $attributes['itemColor'] : '' ) ],
203 + $contentTypography,
204 + );
205 + }
206 +
207 + public function get_list_item_gap_css( $attributes, $device = '' ) {
208 + return array_merge(
209 + Range::get_css([
210 + 'attributeValue' => $attributes['listItemGap'],
211 + 'attribute_object_key' => 'value',
212 + 'isResponsive' => false,
213 + 'defaultValue' => 30,
214 + 'unitDefaultValue' => 'px',
215 + 'property' => 'line-height',
216 + 'hasUnit' => true,
217 + 'device' => $device,
218 + ]),
219 + );
220 + }
221 + public function get_active_list_item_css( $attributes, $device = '' ) {
222 + return array_merge(
223 + [ 'color' => Color::get_css( isset( $attributes['activeColor'] ) ? $attributes['activeColor'] : '' ) ],
224 + );
225 + }
226 +
227 +
228 + public function get_marker_list_style_css( $attributes ) {
229 + $css = [];
230 + $allowed_list_types = [
231 + 'decimal',
232 + 'disc',
233 + 'circle',
234 + 'square',
235 + 'lower-alpha',
236 + 'lower-roman',
237 + 'none'
238 + ];
239 + if ( ! empty( $attributes['markerView'] ) && in_array( $attributes['markerView'], $allowed_list_types, true ) ) {
240 + $css['list-style-type'] = $attributes['markerView'];
241 + }
242 + return $css;
243 + }
244 +
245 +
246 +
247 + public function add_toc_to_post_content( $content ) {
248 +
249 + $content = preg_replace_callback('/<h([1-6])(.*?)>(.*?)<\/h\1>/i', function ( $matches ) {
250 + $level = $matches[1]; // Heading level (1-6)
251 + $attributes = $matches[2]; // Existing attributes (classes, styles, etc.)
252 + $heading = $matches[3]; // Heading text/content
253 + $anchor = sanitize_title( $heading ); // Generate a sanitized ID
254 + // Check if the id attribute is already present
255 + if ( strpos( $attributes, 'id=' ) === false ) {
256 + // Insert the id attribute after the opening <h> tag, preserving existing attributes
257 + return '<h' . $level . $attributes . ' id="' . $anchor . '">' . $heading . '</h' . $level . '>';
258 + }
259 + return $matches[0]; // If id is already present, return the original match
260 + }, $content);
261 + return $content;
262 + }
263 +
264 +
265 + public function render_block_content( $attributes, $content, $block_instance ) {
266 + $post = get_post();
267 + if ( ! $post ) {
268 + return '';
269 + }
270 +
271 + add_filter( 'the_content', [ $this, 'add_toc_to_post_content' ] );
272 +
273 + // Sanitize and escape icon attributes
274 + $open_icon_attributes = array(
275 + 'path' => esc_attr( $attributes['openIconSvgPath'] ),
276 + 'viewBox' => esc_attr( $attributes['openIconSvgViewBox'] ),
277 + 'className' => sanitize_text_field( $attributes['openIconClass'] ),
278 + 'width' => '20',
279 + 'height' => '20',
280 + );
281 + $close_icon_attributes = array(
282 + 'path' => esc_attr( $attributes['closeIconSvgPath'] ),
283 + 'viewBox' => esc_attr( $attributes['closeIconSvgViewBox'] ),
284 + 'className' => sanitize_text_field( $attributes['closeIconClass'] ),
285 + 'width' => '20',
286 + 'height' => '20',
287 + );
288 +
289 + $post_content = $post->post_content;
290 + preg_match_all( '/<h([1-6])[^>]*>(.*?)<\/h\1>/', $post_content, $matches, PREG_SET_ORDER );
291 + $toc = '';
292 +
293 + $headings = [];
294 + $unique_anchors = [];
295 +
296 + // Process headings
297 + foreach ( $matches as $match ) {
298 + $level = intval( $match[1] );
299 + $heading_html = trim( $match[2] );
300 + $heading = esc_html( wp_strip_all_tags( $heading_html ) );
301 +
302 + $base_anchor = strtolower( sanitize_title( $heading ) );
303 + $anchor = $base_anchor;
304 + $count = 1;
305 +
306 + while ( in_array( $anchor, $unique_anchors, true ) ) {
307 + $anchor = $base_anchor . '-' . $count;
308 + $count++;
309 + }
310 +
311 + if ( (
312 + ( $level === 1 && $attributes['H1'] ) ||
313 + ( $level === 2 && $attributes['H2'] ) ||
314 + ( $level === 3 && $attributes['H3'] ) ||
315 + ( $level === 4 && $attributes['H4'] ) ||
316 + ( $level === 5 && $attributes['H5'] ) ||
317 + ( $level === 6 && $attributes['H6'] ) ) ) {
318 +
319 + $headings[] = [
320 + 'level' => $level,
321 + 'heading' => $heading,
322 + 'anchor' => esc_attr( $anchor ),
323 + ];
324 + $unique_anchors[] = $anchor;
325 + }
326 + }//end foreach
327 +
328 + $toc_list = $this->generate_toc_list( $attributes, $headings );
329 + $has_toc = $toc_list !== '';
330 +
331 + // Build TOC header
332 + if ( (bool) $attributes['hideTitle'] === true ) :
333 + $toc .= '<div class="ablocks-toc__header">';
334 + $toc .= $has_toc ? '<span class="ablocks-toc__header-title">' . esc_html( $attributes['tocTableTitle'] ) . '</span>' : '';
335 + if ( $attributes['collapSible'] ) :
336 + $toc .= '<div class="ablocks-toc__header-toggle-icon">';
337 + $toc .= '<span class="ablocks-toc__show">' . Helper::render_svg_icon_using_attr( $close_icon_attributes ) . '</span>';
338 + $toc .= '<span class="ablocks-toc__hide">' . Helper::render_svg_icon_using_attr( $open_icon_attributes ) . '</span>';
339 + $toc .= '</div>';
340 + endif;
341 + $toc .= '</div>';
342 + endif;
343 +
344 + // Build TOC body
345 + $toc .= '<div class="ablocks-toc-body">';
346 + $toc .= $toc_list;
347 + $toc .= '</div>';
348 +
349 + return $toc;
350 + }
351 +
352 + private function generate_toc_list( $attributes, $headings ) {
353 + if ( empty( $headings ) ) {
354 + return '';
355 + }
356 +
357 + $toc = '';
358 + $marker_view = 'ol';
359 + $current_level = 0;
360 + $open_lists = [];
361 + foreach ( $headings as $index => $heading ) {
362 + if ( ! isset( $heading['level'], $heading['heading'], $heading['anchor'] ) ) {
363 + continue;
364 + }
365 +
366 + $level = (int) $heading['level'];
367 +
368 + // If the first item, open the root list
369 + if ( $index === 0 ) {
370 + $toc .= '<' . esc_attr( $marker_view ) . ' class="ablocks-toc-list">';
371 + $open_lists[] = $marker_view;
372 + $current_level = $level;
373 + }
374 +
375 + // If deeper heading level, open a nested list
376 + while ( $level > $current_level ) {
377 + $toc .= '<' . esc_attr( $marker_view ) . ' class="ablocks-toc-list">';
378 + $open_lists[] = $marker_view;
379 + $current_level++;
380 + }
381 +
382 + // If shallower heading level, close open lists
383 + while ( $level < $current_level ) {
384 + $toc .= '</' . esc_attr( array_pop( $open_lists ) ) . '>';
385 + $current_level--;
386 + }
387 +
388 + // Close previous <li> before adding a new one (except for the first)
389 + if ( $index > 0 ) {
390 + $toc .= '</li>';
391 + }
392 +
393 + // Add list item
394 + $toc .= '<li class="ablocks-toc-item">';
395 + $toc .= '<a class="ablocks-toc-item-link" href="#' . esc_attr( $heading['anchor'] ) . '">' . esc_html( $heading['heading'] ) . '</a>';
396 + }//end foreach
397 +
398 + // Close any remaining open lists
399 + while ( ! empty( $open_lists ) ) {
400 + $toc .= '</li></' . esc_attr( array_pop( $open_lists ) ) . '>';
401 + }
402 +
403 + return $toc;
404 + }
405 +}