PluginProbe
King Addons for Elementor – 100+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce Builder, Mega Menu, Popup Builder / 51.1.81
King Addons for Elementor – 100+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce Builder, Mega Menu, Popup Builder v51.1.81
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 51.1.45 All 40 releases
king-addons / includes / wishlist / Wishlist_Renderer.php

Wishlist_Renderer.php in King Addons for Elementor – 100+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce Builder, Mega Menu, Popup Builder 51.1.81, at includes/wishlist/Wishlist_Renderer.php

216 lines 7.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace King_Addons\Wishlist;
4
5 if (!defined('ABSPATH')) {
6 exit;
7 }
8
9 /**
10 * Handles HTML rendering for wishlist components.
11 */
12 class Wishlist_Renderer
13 {
14 private Wishlist_Service $service;
15
16 /**
17 * Setup renderer with service dependency.
18 *
19 * @param Wishlist_Service $service Wishlist service instance.
20 */
21 public function __construct(Wishlist_Service $service)
22 {
23 $this->service = $service;
24 }
25
26 /**
27 * Render wishlist button markup.
28 *
29 * @param array<string, mixed> $args Rendering arguments.
30 * @return string Rendered HTML.
31 */
32 public function render_button(array $args = []): string
33 {
34 if (!Wishlist_Settings::is_enabled()) {
35 return '';
36 }
37
38 if (!Wishlist_Settings::guests_allowed() && !is_user_logged_in()) {
39 $message = Wishlist_Settings::get('guest_block_text');
40 return '<div class="king-addons-wishlist__notice">' . esc_html($message) . '</div>';
41 }
42
43 $defaults = [
44 'product_id' => get_the_ID(),
45 'variation_id' => 0,
46 'wishlist_id' => null,
47 'class' => '',
48 ];
49 $args = wp_parse_args($args, $defaults);
50
51 $product_id = absint($args['product_id']);
52 $variation_id = absint($args['variation_id']);
53 if ($product_id <= 0) {
54 return '';
55 }
56
57 if (function_exists('wc_get_product') && !wc_get_product($product_id)) {
58 return '';
59 }
60
61 $wishlist_id = !empty($args['wishlist_id']) ? sanitize_title((string) $args['wishlist_id']) : $this->service->get_active_wishlist_id();
62
63 $in_list = $this->service->has_item($product_id, $variation_id, $wishlist_id);
64 $label_default = $args['label_default'] ?? Wishlist_Settings::get('button_add_text');
65 $label_added = $args['label_added'] ?? Wishlist_Settings::get('button_added_text');
66 $label = $in_list ? $label_added : $label_default;
67 $state_class = $in_list ? 'king-addons-wishlist-button--added' : 'king-addons-wishlist-button--default';
68
69 $display_mode = $args['display_mode'] ?? Wishlist_Settings::get('button_display_mode', 'icon_text');
70 $show_icon = in_array($display_mode, ['icon', 'icon_text'], true);
71 $show_label = $display_mode === 'icon_text';
72 $icon_class = (string) ($args['icon_class'] ?? Wishlist_Settings::get('icon_choice', 'fas fa-heart'));
73
74 $classes = [
75 'king-addons-wishlist-button',
76 $state_class,
77 sanitize_html_class((string) $args['class']),
78 ];
79
80 $icon_html = $show_icon ? $this->render_icon_html($icon_class) : '';
81 $label_html = $show_label ? '<span class="king-addons-wishlist-button__label">' . esc_html($label) . '</span>' : '';
82
83 return '<button type="button" class="' . esc_attr(implode(' ', array_filter($classes))) . '" data-product-id="' . esc_attr((string) $product_id) . '" data-variation-id="' . esc_attr((string) $variation_id) . '" data-wishlist-id="' . esc_attr((string) $wishlist_id) . '" data-state="' . esc_attr($in_list ? 'added' : 'default') . '" data-display-mode="' . esc_attr((string) $display_mode) . '" data-label-default="' . esc_attr((string) $label_default) . '" data-label-added="' . esc_attr((string) $label_added) . '" aria-pressed="' . ($in_list ? 'true' : 'false') . '" aria-label="' . esc_attr((string) $label) . '">' . $icon_html . $label_html . '</button>';
84 }
85
86 /**
87 * Built-in heart SVG used by the button and floating icon.
88 *
89 * @param string $class Extra SVG class.
90 * @return string SVG markup.
91 */
92 public static function heart_svg(string $class = 'king-addons-wishlist-button__heart'): string
93 {
94 return '<svg class="' . esc_attr($class) . '" viewBox="0 0 24 24" width="16" height="16" focusable="false" aria-hidden="true">'
95 . '<path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78L12 21.23l8.84-8.84a5.5 5.5 0 0 0 0-7.78z"/>'
96 . '</svg>';
97 }
98
99 /**
100 * Render the button icon: built-in SVG heart, or a custom font class.
101 *
102 * @param string $icon_class Icon class from settings/widget.
103 * @return string Icon markup.
104 */
105 private function render_icon_html(string $icon_class): string
106 {
107 if ($this->uses_builtin_heart($icon_class)) {
108 return '<span class="king-addons-wishlist-button__icon" aria-hidden="true">' . self::heart_svg() . '</span>';
109 }
110
111 $classes = preg_split('/\s+/', trim($icon_class), -1, PREG_SPLIT_NO_EMPTY);
112 $safe = implode(' ', array_map('sanitize_html_class', is_array($classes) ? $classes : []));
113 if ($safe === '') {
114 return $this->render_icon_html('fas fa-heart');
115 }
116
117 return '<span class="king-addons-wishlist-button__icon" aria-hidden="true"><i class="' . esc_attr($safe) . '"></i></span>';
118 }
119
120 /**
121 * Default heart classes use the built-in SVG instead of a webfont.
122 *
123 * @param string $icon_class Icon class from settings/widget.
124 * @return bool Whether to render the SVG heart.
125 */
126 private function uses_builtin_heart(string $icon_class): bool
127 {
128 $normalized = strtolower(trim(preg_replace('/\s+/', ' ', $icon_class) ?? $icon_class));
129
130 return in_array($normalized, [
131 '',
132 'fas fa-heart',
133 'fa fa-heart',
134 'far fa-heart',
135 'fa-solid fa-heart',
136 'fa-regular fa-heart',
137 'eicon-heart',
138 'eicon-heart-o',
139 ], true);
140 }
141
142 /**
143 * Sanitize renderer HTML for echo contexts that still want kses.
144 *
145 * @param string $html Button or similar markup.
146 * @return string Sanitized HTML.
147 */
148 public static function kses(string $html): string
149 {
150 $allowed = [
151 'button' => [
152 'type' => true,
153 'class' => true,
154 'disabled' => true,
155 'aria-pressed' => true,
156 'aria-label' => true,
157 ],
158 'span' => [
159 'class' => true,
160 'aria-hidden' => true,
161 ],
162 'i' => [
163 'class' => true,
164 'aria-hidden' => true,
165 ],
166 'svg' => [
167 'class' => true,
168 'viewbox' => true,
169 'width' => true,
170 'height' => true,
171 'focusable' => true,
172 'aria-hidden' => true,
173 ],
174 'path' => [
175 'd' => true,
176 ],
177 ];
178
179 foreach ($allowed as $tag => $attrs) {
180 $allowed[$tag] = array_merge($attrs, [
181 'class' => true,
182 'aria-hidden' => true,
183 'data-*' => true,
184 ]);
185 }
186
187 return wp_kses($html, $allowed);
188 }
189
190 /**
191 * Render wishlist counter element.
192 *
193 * @param array<string, mixed> $args Rendering arguments.
194 * @return string Rendered HTML.
195 */
196 public function render_counter(array $args = []): string
197 {
198 if (!Wishlist_Settings::is_enabled()) {
199 return '';
200 }
201
202 $wishlist_id = !empty($args['wishlist_id']) ? sanitize_title((string) $args['wishlist_id']) : $this->service->get_active_wishlist_id();
203 $count = $this->service->get_count($wishlist_id);
204
205 $classes = [
206 'king-addons-wishlist-counter',
207 isset($args['class']) ? sanitize_html_class($args['class']) : '',
208 ];
209
210 return '<span class="' . esc_attr(implode(' ', array_filter($classes))) . '" data-wishlist-id="' . esc_attr($wishlist_id) . '" data-count="' . esc_attr($count) . '">' . esc_html($count) . '</span>';
211 }
212 }
213
214
215
216