PluginProbe
King Addons for Elementor – 100+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce Builder, Mega Menu, Popup Builder / 51.1.77
King Addons for Elementor – 100+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce Builder, Mega Menu, Popup Builder v51.1.77
51.1.87 51.1.86 51.1.84 51.1.85 51.1.83 51.1.82 51.1.81 51.1.79 51.1.78 51.1.77 51.1.76 51.1.74 51.1.75 51.1.65 51.1.64 51.1.63 trunk 51.1.14 51.1.2 51.1.35 51.1.36 51.1.37 51.1.38 51.1.39 51.1.44 All 41 releases
king-addons / includes / widgets / Woo_Product_Tabs / Woo_Product_Tabs.php

Woo_Product_Tabs.php in King Addons for Elementor – 100+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce Builder, Mega Menu, Popup Builder 51.1.77, at includes/widgets/Woo_Product_Tabs/Woo_Product_Tabs.php

459 lines 15.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Woo Product Tabs widget.
4 *
5 * @package King_Addons
6 */
7
8 namespace King_Addons;
9
10 use Elementor\Controls_Manager;
11 use Elementor\Group_Control_Typography;
12 use Elementor\Repeater;
13
14 if (!defined('ABSPATH')) {
15 exit;
16 }
17
18 /**
19 * Renders WooCommerce product tabs.
20 */
21 class Woo_Product_Tabs extends Abstract_Single_Widget
22 {
23 /**
24 * Whether AJAX handlers are already registered.
25 *
26 * @var bool
27 */
28 private static $ajax_actions_registered = false;
29
30 /**
31 * Constructor.
32 *
33 * @param array<mixed> $data Widget data.
34 * @param array|null $args Widget args.
35 */
36 public function __construct($data = [], $args = null)
37 {
38 parent::__construct($data, $args);
39
40 if (!self::$ajax_actions_registered) {
41 add_action('wp_ajax_king_addons_render_product_tab', [self::class, 'ajax_render_tab']);
42 add_action('wp_ajax_nopriv_king_addons_render_product_tab', [self::class, 'ajax_render_tab']);
43 self::$ajax_actions_registered = true;
44 }
45 }
46
47 public function get_name(): string
48 {
49 return 'woo_product_tabs';
50 }
51
52 public function get_title(): string
53 {
54 return esc_html__('Product Tabs', 'king-addons');
55 }
56
57 public function get_icon(): string
58 {
59 return 'eicon-tabs';
60 }
61
62 public function get_categories(): array
63 {
64 return ['king-addons-woo-builder'];
65 }
66
67 public function get_style_depends(): array
68 {
69 return [KING_ADDONS_ASSETS_UNIQUE_KEY . '-woo-product-tabs-style'];
70 }
71
72 public function get_script_depends(): array
73 {
74 return [KING_ADDONS_ASSETS_UNIQUE_KEY . '-woo-product-tabs-script'];
75 }
76
77 protected function register_controls(): void
78 {
79 $this->start_controls_section(
80 'section_content',
81 [
82 'label' => esc_html__('Content', 'king-addons'),
83 'tab' => Controls_Manager::TAB_CONTENT,
84 ]
85 );
86
87 $this->add_control(
88 'layout',
89 [
90 'label' => esc_html__('Layout', 'king-addons'),
91 'type' => Controls_Manager::SELECT,
92 'options' => [
93 'horizontal' => esc_html__('Horizontal', 'king-addons'),
94 'vertical' => sprintf(__('Vertical %s', 'king-addons'), '<i class="eicon-pro-icon"></i>'),
95 'accordion' => sprintf(__('Accordion %s', 'king-addons'), '<i class="eicon-pro-icon"></i>'),
96 ],
97 'default' => 'horizontal',
98 ]
99 );
100
101 $this->add_control(
102 'tabs_selection',
103 [
104 'label' => sprintf(__('Tabs to show %s', 'king-addons'), '<i class="eicon-pro-icon"></i>'),
105 'type' => Controls_Manager::SELECT2,
106 'multiple' => true,
107 'label_block' => true,
108 'options' => [
109 'description' => esc_html__('Description', 'king-addons'),
110 'additional_information' => esc_html__('Additional Information', 'king-addons'),
111 'reviews' => esc_html__('Reviews', 'king-addons'),
112 ],
113 'default' => ['description', 'additional_information', 'reviews'],
114 ]
115 );
116
117 $repeater = new Repeater();
118 $repeater->add_control(
119 'tab_key',
120 [
121 'label' => esc_html__('Tab', 'king-addons'),
122 'type' => Controls_Manager::SELECT,
123 'options' => [
124 'description' => esc_html__('Description', 'king-addons'),
125 'additional_information' => esc_html__('Additional Information', 'king-addons'),
126 'reviews' => esc_html__('Reviews', 'king-addons'),
127 ],
128 'default' => 'description',
129 ]
130 );
131
132 $repeater->add_control(
133 'custom_label',
134 [
135 'label' => esc_html__('Custom label', 'king-addons'),
136 'type' => Controls_Manager::TEXT,
137 ]
138 );
139
140 $repeater->add_control(
141 'enabled',
142 [
143 'label' => esc_html__('Show tab', 'king-addons'),
144 'type' => Controls_Manager::SWITCHER,
145 'return_value' => 'yes',
146 'default' => 'yes',
147 ]
148 );
149
150 $this->add_control(
151 'tabs_manager',
152 [
153 'label' => sprintf(__('Tabs manager %s', 'king-addons'), '<i class="eicon-pro-icon"></i>'),
154 'type' => Controls_Manager::REPEATER,
155 'fields' => $repeater->get_controls(),
156 'default' => [
157 ['tab_key' => 'description', 'enabled' => 'yes'],
158 ['tab_key' => 'additional_information', 'enabled' => 'yes'],
159 ['tab_key' => 'reviews', 'enabled' => 'yes'],
160 ],
161 'title_field' => '{{ tab_key }}',
162 ]
163 );
164
165 $this->add_control(
166 'active_tab',
167 [
168 'label' => esc_html__('Default Active Tab', 'king-addons'),
169 'type' => Controls_Manager::SELECT,
170 'options' => [
171 'description' => esc_html__('Description', 'king-addons'),
172 'additional_information' => esc_html__('Additional Information', 'king-addons'),
173 'reviews' => esc_html__('Reviews', 'king-addons'),
174 ],
175 'default' => 'description',
176 ]
177 );
178
179 $this->add_control(
180 'ajax_load',
181 [
182 'label' => sprintf(__('AJAX load content %s', 'king-addons'), '<i class="eicon-pro-icon"></i>'),
183 'type' => Controls_Manager::SWITCHER,
184 'return_value' => 'yes',
185 ]
186 );
187
188 $this->end_controls_section();
189
190 $this->start_controls_section(
191 'section_style_tabs',
192 [
193 'label' => esc_html__('Tabs', 'king-addons'),
194 'tab' => Controls_Manager::TAB_STYLE,
195 ]
196 );
197
198 $this->add_group_control(
199 Group_Control_Typography::get_type(),
200 [
201 'name' => 'tabs_typo',
202 'selector' => '{{WRAPPER}} .ka-woo-tabs__nav button',
203 ]
204 );
205
206 $this->add_control(
207 'tabs_color',
208 [
209 'label' => esc_html__('Text Color', 'king-addons'),
210 'type' => Controls_Manager::COLOR,
211 'selectors' => [
212 '{{WRAPPER}} .ka-woo-tabs__nav button' => 'color: {{VALUE}};',
213 ],
214 ]
215 );
216
217 $this->add_control(
218 'tabs_color_active',
219 [
220 'label' => esc_html__('Active Text Color', 'king-addons'),
221 'type' => Controls_Manager::COLOR,
222 'selectors' => [
223 '{{WRAPPER}} .ka-woo-tabs__nav button.is-active' => 'color: {{VALUE}};',
224 ],
225 ]
226 );
227
228 $this->add_control(
229 'tabs_border_active',
230 [
231 'label' => esc_html__('Active Border Color', 'king-addons'),
232 'type' => Controls_Manager::COLOR,
233 'selectors' => [
234 '{{WRAPPER}} .ka-woo-tabs__nav button.is-active' => 'border-color: {{VALUE}};',
235 ],
236 ]
237 );
238
239 $this->add_responsive_control(
240 'tabs_gap',
241 [
242 'label' => esc_html__('Tabs Gap', 'king-addons'),
243 'type' => Controls_Manager::SLIDER,
244 'range' => [
245 'px' => ['min' => 0, 'max' => 30],
246 ],
247 'selectors' => [
248 '{{WRAPPER}} .ka-woo-tabs__nav' => 'gap: {{SIZE}}{{UNIT}};',
249 ],
250 ]
251 );
252
253 $this->end_controls_section();
254
255 $this->start_controls_section(
256 'section_style_content',
257 [
258 'label' => esc_html__('Content', 'king-addons'),
259 'tab' => Controls_Manager::TAB_STYLE,
260 ]
261 );
262
263 $this->add_group_control(
264 Group_Control_Typography::get_type(),
265 [
266 'name' => 'content_typo',
267 'selector' => '{{WRAPPER}} .ka-woo-tabs__panel',
268 ]
269 );
270
271 $this->add_control(
272 'content_color',
273 [
274 'label' => esc_html__('Content Color', 'king-addons'),
275 'type' => Controls_Manager::COLOR,
276 'selectors' => [
277 '{{WRAPPER}} .ka-woo-tabs__panel' => 'color: {{VALUE}};',
278 ],
279 ]
280 );
281
282 $this->add_responsive_control(
283 'content_padding',
284 [
285 'label' => esc_html__('Content Padding', 'king-addons'),
286 'type' => Controls_Manager::DIMENSIONS,
287 'size_units' => ['px', 'em', '%'],
288 'selectors' => [
289 '{{WRAPPER}} .ka-woo-tabs__panel' => 'padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};',
290 ],
291 ]
292 );
293
294 $this->end_controls_section();
295 }
296
297 protected function render(): void
298 {
299 $product = $this->get_product();
300 if (!$product) {
301 $this->render_missing_product_notice();
302 return;
303 }
304
305 global $product;
306 $settings = $this->get_settings_for_display();
307 $can_pro = king_addons_can_use_pro();
308
309 $layout = $settings['layout'] ?? 'horizontal';
310 if (in_array($layout, ['vertical', 'accordion'], true) && !$can_pro) {
311 $layout = 'horizontal';
312 }
313
314 $manager_rows = $settings['tabs_manager'] ?? [];
315 $allowed_tabs = $settings['tabs_selection'] ?? ['description', 'additional_information', 'reviews'];
316 if (!$can_pro || empty($allowed_tabs)) {
317 $allowed_tabs = ['description', 'additional_information', 'reviews'];
318 }
319
320 $active_tab = $settings['active_tab'] ?? 'description';
321 $ajax = !empty($settings['ajax_load']) && $can_pro;
322
323 $tabs = apply_filters('woocommerce_product_tabs', []);
324 if (empty($tabs)) {
325 return;
326 }
327
328 // Use manager order if enabled and Pro, otherwise fallback to selection list.
329 if ($can_pro && !empty($manager_rows)) {
330 $ordered_tabs = [];
331 foreach ($manager_rows as $row) {
332 if (empty($row['tab_key'])) {
333 continue;
334 }
335 $key = sanitize_key($row['tab_key']);
336 $enabled = !isset($row['enabled']) || 'yes' === $row['enabled'];
337 if (!$enabled || !isset($tabs[$key])) {
338 continue;
339 }
340 if (!empty($row['custom_label'])) {
341 $tabs[$key]['title'] = esc_html($row['custom_label']);
342 }
343 $ordered_tabs[$key] = $tabs[$key];
344 }
345 // If manager removed everything, keep original.
346 if (!empty($ordered_tabs)) {
347 $tabs = $ordered_tabs;
348 }
349 } else {
350 $ordered_tabs = [];
351 foreach ($allowed_tabs as $key) {
352 if (isset($tabs[$key])) {
353 $ordered_tabs[$key] = $tabs[$key];
354 }
355 }
356 // Append any remaining tabs to avoid losing 3rd-party ones.
357 foreach ($tabs as $key => $tab) {
358 if (!isset($ordered_tabs[$key])) {
359 $ordered_tabs[$key] = $tab;
360 }
361 }
362 $tabs = $ordered_tabs;
363 }
364
365 if (!isset($tabs[$active_tab])) {
366 $active_tab = array_key_first($tabs);
367 }
368
369 $wrapper_classes = ['ka-woo-tabs', 'ka-woo-tabs--' . $layout];
370 echo '<div class="' . esc_attr(implode(' ', $wrapper_classes)) . '" data-active="' . esc_attr($active_tab) . '" data-ajax="' . ($ajax ? 'yes' : 'no') . '" data-ajax-url="' . esc_url(admin_url('admin-ajax.php')) . '" data-nonce="' . esc_attr(wp_create_nonce('king_addons_woo_tabs')) . '" data-product-id="' . esc_attr($product->get_id()) . '">';
371
372 echo '<div class="ka-woo-tabs__nav">';
373 foreach ($tabs as $key => $tab) {
374 $is_active = $key === $active_tab ? ' is-active' : '';
375 echo '<button type="button" class="ka-woo-tabs__tab' . $is_active . '" data-tab="' . esc_attr($key) . '">' . wp_kses_post($tab['title']) . '</button>';
376 }
377 echo '</div>';
378
379 echo '<div class="ka-woo-tabs__panels">';
380 foreach ($tabs as $key => $tab) {
381 $is_active = $key === $active_tab ? ' is-active' : '';
382 echo '<div class="ka-woo-tabs__panel' . $is_active . '" data-tab="' . esc_attr($key) . '">';
383 if ('accordion' === $layout) {
384 echo '<button type="button" class="ka-woo-tabs__accordion-toggle" data-tab="' . esc_attr($key) . '">' . wp_kses_post($tab['title']) . '</button>';
385 echo '<div class="ka-woo-tabs__accordion-body">';
386 }
387 if ($ajax && !$is_active) {
388 echo '<div class="ka-woo-tabs__placeholder"></div>';
389 } else {
390 if (isset($tab['callback'])) {
391 call_user_func($tab['callback'], $key, $tab);
392 }
393 }
394 if ('accordion' === $layout) {
395 echo '</div>';
396 }
397 echo '</div>';
398 }
399 echo '</div>';
400
401 echo '</div>';
402 }
403
404 /**
405 * AJAX render a single tab.
406 */
407 public static function ajax_render_tab(): void
408 {
409 $nonce = isset($_POST['nonce']) ? sanitize_text_field(wp_unslash($_POST['nonce'])) : '';
410 if (!wp_verify_nonce($nonce, 'king_addons_woo_tabs')) {
411 wp_send_json_error(['message' => esc_html__('Invalid nonce.', 'king-addons')], 400);
412 }
413
414 if (!class_exists('WooCommerce') || !function_exists('wc_get_product')) {
415 wp_send_json_error(['message' => esc_html__('WooCommerce is not available.', 'king-addons')], 400);
416 }
417
418 $product_id = isset($_POST['product_id']) ? (int) $_POST['product_id'] : 0;
419 $tab_key = isset($_POST['tab_key']) ? sanitize_key(wp_unslash($_POST['tab_key'])) : '';
420
421 if ($product_id <= 0 || empty($tab_key)) {
422 wp_send_json_error(['message' => esc_html__('Invalid request.', 'king-addons')], 400);
423 }
424
425 $product = wc_get_product($product_id);
426 if (!$product) {
427 wp_send_json_error(['message' => esc_html__('Product not found.', 'king-addons')], 404);
428 }
429
430 $previous_product = $GLOBALS['product'] ?? null;
431 $GLOBALS['product'] = $product;
432
433 $tabs = apply_filters('woocommerce_product_tabs', []);
434 if (!isset($tabs[$tab_key]) || empty($tabs[$tab_key]['callback'])) {
435 $GLOBALS['product'] = $previous_product;
436 wp_send_json_error(['message' => esc_html__('Tab not found.', 'king-addons')], 404);
437 }
438
439 ob_start();
440 call_user_func($tabs[$tab_key]['callback'], $tab_key, $tabs[$tab_key]);
441 $html = ob_get_clean();
442
443 $GLOBALS['product'] = $previous_product;
444
445 wp_send_json_success(
446 [
447 'html' => $html,
448 ]
449 );
450 }
451 }
452
453
454
455
456
457
458
459