PluginProbe
aBlocks – Gutenberg Blocks, User Dashboard Builder, Popup Builder, Form Builder & Animation Builder / 2.10.0
aBlocks – Gutenberg Blocks, User Dashboard Builder, Popup Builder, Form Builder & Animation Builder v2.10.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
ablocks / includes / blocks / table-of-content / block.php

block.php in aBlocks – Gutenberg Blocks, User Dashboard Builder, Popup Builder, Form Builder & Animation Builder 2.10.0, at includes/blocks/table-of-content/block.php

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