PluginProbe
aBlocks – Gutenberg Blocks, User Dashboard Builder, Popup Builder, Form Builder & Animation Builder / 2.9.1
aBlocks – Gutenberg Blocks, User Dashboard Builder, Popup Builder, Form Builder & Animation Builder v2.9.1
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 1.1.2 All 80 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.9.1, at includes/blocks/table-of-content/block.php

308 lines 9.5 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\Helper;
11 use ABlocks\Controls\Typography;
12 use ABlocks\Controls\Background;
13 use ABlocks\Controls\Border;
14 use ABlocks\Controls\Dimensions;
15 use ABlocks\Controls\Alignment;
16 use ABlocks\Controls\Range;
17
18 class Block extends BlockBaseAbstract {
19 protected $block_name = 'table-of-content';
20
21
22 public function build_css( $attributes ) {
23 $css_generator = new CssGenerator( $attributes );
24
25 $css_generator->add_class_styles(
26 '{{WRAPPER}} .ablocks-toc__header-title',
27 $this->get_toc_title_css( $attributes ),
28 $this->get_toc_title_css( $attributes, 'Tablet' ),
29 $this->get_toc_title_css( $attributes, 'Mobile' )
30 );
31 $css_generator->add_class_styles(
32 '{{WRAPPER}} .ablocks-toc__header',
33 $this->get_toc_header_css( $attributes ),
34 $this->get_toc_header_css( $attributes, 'Tablet' ),
35 $this->get_toc_header_css( $attributes, 'Mobile' )
36 );
37 $css_generator->add_class_styles(
38 '{{WRAPPER}} .ablocks-toc-body',
39 $this->get_toc_body_css( $attributes ),
40 $this->get_toc_body_css( $attributes, 'Tablet' ),
41 $this->get_toc_body_css( $attributes, 'Mobile' )
42 );
43 $css_generator->add_class_styles(
44 '{{WRAPPER}} .ablocks-toc__header-toggle-icon svg',
45 $this->get_toc_header_icon_css( $attributes ),
46 $this->get_toc_header_icon_css( $attributes, 'Tablet' ),
47 $this->get_toc_header_icon_css( $attributes, 'Mobile' )
48 );
49 $css_generator->add_class_styles(
50 '{{WRAPPER}} .ablocks-block-container .ablocks-toc-list,
51 {{WRAPPER}} .ablocks-block-container .ablocks-toc-list li a',
52 $this->get_list_item_css( $attributes ),
53 $this->get_list_item_css( $attributes, 'Tablet' ),
54 $this->get_list_item_css( $attributes, 'Mobile' )
55 );
56
57 return $css_generator->generate_css();
58 }
59
60
61
62 public function get_toc_title_css( $attributes, $device = '' ) {
63 $css = array();
64 $toc_title_typography_css = ! empty( $attributes['titleTypography'] ) ? Typography::get_css( $attributes['titleTypography'], '', $device ) : array();
65 if ( isset( $attributes['titleColor'] ) ) {
66 $css['color'] = $attributes['titleColor'];
67 }
68 return array_merge( $toc_title_typography_css, $css );
69 }
70 public function get_toc_header_css( $attributes, $device = '' ) {
71 $css = array();
72 $headerorder = ! empty( $attributes['headerBorder'] ) ? Border::get_css( $attributes['headerBorder'], '', $device ) : array();
73 $header_padding_css = ! empty( $attributes['header_padding'] ) ? Dimensions::get_css( $attributes['header_padding'], 'padding', $device ) : array();
74 if ( isset( $attributes['headerBG'] ) ) {
75 $css['background'] = $attributes['headerBG'];
76 }
77 return array_merge( $css, $headerorder, $header_padding_css );
78 }
79 public function get_toc_body_css( $attributes, $device = '' ) {
80 $css = array();
81 $listing_padding_css = ! empty( $attributes['list_padding'] ) ? Dimensions::get_css( $attributes['list_padding'], 'padding', $device ) : array();
82 if ( isset( $attributes['bodyBG'] ) ) {
83 $css['background'] = $attributes['bodyBG'];
84 }
85 return array_merge( $listing_padding_css, $css );
86 }
87 public function get_toc_header_icon_css( $attributes, $device = '' ) {
88 $css = array_merge(
89 Range::get_css([
90 'attributeValue' => $attributes['iconSize'],
91 'isResponsive' => false,
92 'defaultValue' => 20,
93 'unitDefaultValue' => 'px',
94 'property' => 'width',
95 'device' => $device,
96 ]),
97 Range::get_css([
98 'attributeValue' => $attributes['iconSize'],
99 'isResponsive' => false,
100 'defaultValue' => 20,
101 'unitDefaultValue' => 'px',
102 'property' => 'height',
103 'device' => $device,
104 ]),
105 );
106 if ( ! empty( $attributes['iconSize'] ) ) {
107 $css['width'] = $attributes['iconSize'] . 'px';
108 $css['height'] = $attributes['iconSize'] . 'px';
109 }
110 if ( isset( $attributes['iconColor'] ) ) {
111 $css['fill'] = $attributes['iconColor'];
112 }
113 return $css;
114 }
115
116 public function get_list_item_css( $attributes, $device = '' ) {
117 $css = array();
118
119 $contentTypography = ! empty( $attributes['contentTypography'] ) ? Typography::get_css( $attributes['contentTypography'], '', $device ) : array();
120
121 if ( ! empty( $attributes['itemColor'] ) ) {
122 $css['color'] = $attributes['itemColor'];
123 }
124 return array_merge(
125 Range::get_css([
126 'attributeValue' => $attributes['listItemGap'],
127 'attribute_object_key' => 'value',
128 'isResponsive' => false,
129 'defaultValue' => 30,
130 'unitDefaultValue' => 'px',
131 'property' => 'line-height',
132 'hasUnit' => true,
133 'device' => $device,
134 ]),
135 $contentTypography,
136 $css,
137 );
138 }
139
140
141
142
143 public function add_toc_to_post_content( $content ) {
144
145 $content = preg_replace_callback('/<h([1-6])(.*?)>(.*?)<\/h\1>/i', function ( $matches ) {
146 $level = $matches[1]; // Heading level (1-6)
147 $attributes = $matches[2]; // Existing attributes (classes, styles, etc.)
148 $heading = $matches[3]; // Heading text/content
149 $anchor = sanitize_title( $heading ); // Generate a sanitized ID
150 // Check if the id attribute is already present
151 if ( strpos( $attributes, 'id=' ) === false ) {
152 // Insert the id attribute after the opening <h> tag, preserving existing attributes
153 return '<h' . $level . $attributes . ' id="' . $anchor . '">' . $heading . '</h' . $level . '>';
154 }
155 return $matches[0]; // If id is already present, return the original match
156 }, $content);
157 return $content;
158 }
159
160
161 public function render_block_content( $attributes, $content, $block_instance ) {
162 $post = get_post();
163 if ( ! $post ) {
164 return '';
165 }
166
167 add_filter( 'the_content', [ $this, 'add_toc_to_post_content' ] );
168
169 // Sanitize and escape icon attributes
170 $open_icon_attributes = array(
171 'path' => esc_attr( $attributes['openIconSvgPath'] ),
172 'viewBox' => esc_attr( $attributes['openIconSvgViewBox'] ),
173 'className' => esc_attr( $attributes['openIconClass'] ),
174 'width' => '20',
175 'height' => '20',
176 );
177 $close_icon_attributes = array(
178 'path' => esc_attr( $attributes['closeIconSvgPath'] ),
179 'viewBox' => esc_attr( $attributes['closeIconSvgViewBox'] ),
180 'className' => esc_attr( $attributes['closeIconClass'] ),
181 'width' => '20',
182 'height' => '20',
183 );
184
185 $post_content = $post->post_content;
186 preg_match_all( '/<h([1-6])[^>]*>(.*?)<\/h\1>/', $post_content, $matches, PREG_SET_ORDER );
187 $toc = '';
188
189 // Build TOC header
190 if ( (bool) $attributes['hideTitle'] === true ) :
191 $toc = '<div class="ablocks-toc__header">';
192 $toc .= '<span class="ablocks-toc__header-title">' . esc_html( $attributes['tocTableTitle'] ) . '</span>';
193 if ( $attributes['collapSible'] ) :
194 $toc .= '<div class="ablocks-toc__header-toggle-icon">';
195 $toc .= '<span class="ablocks-toc__show">' . Helper::render_svg_icon_using_attr( $close_icon_attributes ) . '</span>';
196 $toc .= '<span class="ablocks-toc__hide">' . Helper::render_svg_icon_using_attr( $open_icon_attributes ) . '</span>';
197 $toc .= '</div>';
198 endif;
199 $toc .= '</div>';
200 endif;
201
202 $headings = [];
203 $unique_anchors = [];
204
205 // Process headings
206 foreach ( $matches as $match ) {
207 $level = intval( $match[1] );
208 $heading = trim( $match[2] );
209 $base_anchor = strtolower( sanitize_title( $heading ) );
210 $anchor = $base_anchor;
211 $count = 1;
212
213 // Ensure unique anchors
214 while ( in_array( $anchor, $unique_anchors, true ) ) {
215 $anchor = $base_anchor . '-' . $count;
216 $count++;
217 }
218
219 // Add heading if enabled in attributes
220 if ( (
221 ( $level === 1 && $attributes['H1'] ) ||
222 ( $level === 2 && $attributes['H2'] ) ||
223 ( $level === 3 && $attributes['H3'] ) ||
224 ( $level === 4 && $attributes['H4'] ) ||
225 ( $level === 5 && $attributes['H5'] ) ||
226 ( $level === 6 && $attributes['H6'] ) ) ) {
227 $headings[] = [
228 'level' => $level,
229 'heading' => esc_html( $heading ),
230 'anchor' => esc_attr( $anchor ),
231 ];
232 $unique_anchors[] = $anchor;
233 }
234 }//end foreach
235
236 // Build TOC body
237 $toc .= '<div class="ablocks-toc-body">';
238 $toc .= $this->generate_toc_list( $attributes, $headings );
239 $toc .= '</div>';
240
241 return $toc;
242 }
243
244 private function generate_toc_list( $attributes, $headings ) {
245 if ( empty( $headings ) ) {
246 return '';
247 }
248
249 $toc = '';
250 $marker_view = in_array( $attributes['markerView'], [ 'ul', 'ol' ], true ) ? $attributes['markerView'] : 'ul';
251 $current_level = 0;
252 $open_lists = [];
253
254 foreach ( $headings as $index => $heading ) {
255 if ( ! isset( $heading['level'], $heading['heading'], $heading['anchor'] ) ) {
256 continue;
257 }
258
259 $level = (int) $heading['level'];
260
261 // If the first item, open the root list
262 if ( $index === 0 ) {
263 $toc .= '<' . esc_attr( $marker_view ) . ' class="ablocks-toc-list">';
264 $open_lists[] = $marker_view;
265 $current_level = $level;
266 }
267
268 // If deeper heading level, open a nested list
269 while ( $level > $current_level ) {
270 $toc .= '<' . esc_attr( $marker_view ) . ' class="ablocks-toc-list">';
271 $open_lists[] = $marker_view;
272 $current_level++;
273 }
274
275 // If shallower heading level, close open lists
276 while ( $level < $current_level ) {
277 $toc .= '</' . esc_attr( array_pop( $open_lists ) ) . '>';
278 $current_level--;
279 }
280
281 // Close previous <li> before adding a new one (except for the first)
282 if ( $index > 0 ) {
283 $toc .= '</li>';
284 }
285
286 // Add list item
287 $toc .= '<li class="ablocks-toc-item">';
288 $toc .= '<a class="ablocks-toc-item-link" href="#' . esc_attr( $heading['anchor'] ) . '">' . esc_html( $heading['heading'] ) . '</a>';
289 }//end foreach
290
291 // Close any remaining open lists
292 while ( ! empty( $open_lists ) ) {
293 $toc .= '</li></' . esc_attr( array_pop( $open_lists ) ) . '>';
294 }
295
296 return $toc;
297 }
298
299
300
301
302
303
304
305
306
307 }
308