PluginProbe
NotificationX – FOMO, Live Sales Notification, WooCommerce Sales Popup, GDPR, Social Proof, Announcement Banner & Floating Notification Bar / 3.3.1
NotificationX – FOMO, Live Sales Notification, WooCommerce Sales Popup, GDPR, Social Proof, Announcement Banner & Floating Notification Bar v3.3.1
3.3.1 3.3.0 3.2.14 3.2.13 3.2.12 3.2.11 3.2.10 3.2.9 3.2.8 3.2.7 trunk 0.2.5.5 0.2.5.6 0.2.5.7 1.0.0 1.0.1 1.0.2 1.0.3 1.1.0 1.1.1 1.1.2 1.1.3 1.1.4 1.2.0 1.2.1 All 156 releases
notificationx / includes / Extensions / ExitIntent / ExitIntentNotification.php

ExitIntentNotification.php in NotificationX – FOMO, Live Sales Notification, WooCommerce Sales Popup, GDPR, Social Proof, Announcement Banner & Floating Notification Bar 3.3.1, at includes/Extensions/ExitIntent/ExitIntentNotification.php

2,284 lines 128.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Exit Intent Popup Extension
4 *
5 * @package NotificationX\Extensions
6 */
7
8 namespace NotificationX\Extensions\ExitIntent;
9
10 use NotificationX\NotificationX;
11 use NotificationX\GetInstance;
12 use NotificationX\Core\Rules;
13 use NotificationX\Core\Helper;
14 use NotificationX\Extensions\GlobalFields;
15 use NotificationX\Extensions\Extension;
16
17 /**
18 * Exit Intent Popup Extension
19 * @method static ExitIntentNotification get_instance($args = null)
20 */
21 class ExitIntentNotification extends Extension {
22 use GetInstance;
23
24 public $priority = 16;
25 public $id = 'exit_intent_custom';
26 public $doc_link = 'https://notificationx.com/docs/';
27 public $types = 'exit_intent';
28 public $module = 'modules_exit_intent';
29
30 /**
31 * Elementor seed-theme registry for the "Build With Elementor" modal.
32 * Populated alongside the JSON files in jsons/ (see Task 07).
33 *
34 * @var array<string, array{label:string, value:string, icon:string, column:string, title:string}>
35 */
36 public $elementor_themes = [];
37
38 public function __construct() {
39 parent::__construct();
40 add_action( 'init', [ $this, 'register_post_type' ] );
41 add_filter( 'get_edit_post_link', [ $this, 'filter_edit_post_link' ], 10, 3 );
42 add_filter( 'nx_filtered_post', [ $this, 'inject_elementor_html' ], 10, 2 );
43 add_action( 'wp_enqueue_scripts', [ $this, 'enqueue_elementor_assets' ], 20 );
44 add_action( 'wp_head', [ $this, 'print_section_constraint_css' ], 99 );
45 add_action( 'elementor/documents/register_controls', [ $this, 'register_popup_layout_controls' ] );
46 add_action( 'elementor/editor/after_enqueue_scripts', [ $this, 'enqueue_editor_live_preview' ] );
47 }
48
49 /**
50 * Print the popup section's width constraint inline in <head>.
51 *
52 * Two cases:
53 * 1. Sections that have the `nx-exit-intent-section` class baked in via the
54 * seed JSON's css_classes setting — constrained by the first rule.
55 * 2. The Elementor editor preview iframe loads the nx_exit_intent post
56 * directly as its own page (body.single-nx_exit_intent). The second
57 * rule constrains ANY section on that page, including legacy imports
58 * made before css_classes was added to the seed JSON.
59 */
60 public function print_section_constraint_css() {
61 // Width is governed by the Layout panel's Width control via the
62 // `--nx-exit-width` CSS variable; fall back to the historical 540px cap
63 // when it is unset (older popups / default).
64 $vars = '';
65 if ( is_singular( 'nx_exit_intent' ) ) {
66 // Editor preview / direct view: resolve the variable from the saved
67 // Layout-panel settings on this document.
68 $layout = $this->get_popup_layout_settings( get_the_ID() );
69 if ( ! empty( $layout['width'] ) ) {
70 $vars .= '--nx-exit-width:' . $layout['width'] . ';';
71 }
72 if ( ! empty( $layout['height'] ) ) {
73 $vars .= '--nx-exit-height:' . $layout['height'] . ';';
74 }
75 }
76 echo "<style id='nx-exit-intent-section-constraint'>\n";
77 if ( '' !== $vars ) {
78 echo "body.single-nx_exit_intent{" . esc_html( $vars ) . "}\n";
79 }
80 echo ".nx-exit-intent-section{width:100%!important;max-width:var(--nx-exit-width,540px)!important;margin-left:auto!important;margin-right:auto!important;}\n";
81 if ( is_singular( 'nx_exit_intent' ) ) {
82 echo "body.single-nx_exit_intent .elementor-section,body.single-nx_exit_intent .e-con{width:100%!important;max-width:var(--nx-exit-width,540px)!important;margin-left:auto!important;margin-right:auto!important;}\n";
83 }
84 echo "</style>\n";
85 }
86
87 /**
88 * Server-side: inject the Elementor-rendered HTML + a `mode` flag into the
89 * REST payload so the React popup shell can render it.
90 *
91 * Runs for every campaign settings array via the `nx_filtered_post` filter;
92 * scoped to Exit Intent campaigns that have a linked, published nx_exit_intent.
93 */
94 public function inject_elementor_html( $settings, $params ) {
95 if ( empty( $settings['source'] ) || $settings['source'] !== $this->id ) {
96 return $settings;
97 }
98 $elementor_id = isset( $settings['elementor_id'] ) ? (int) $settings['elementor_id'] : 0;
99 if ( ! $elementor_id || ! class_exists( '\\Elementor\\Plugin' ) ) {
100 $settings['mode'] = 'built_in';
101 return $settings;
102 }
103 if ( get_post_status( $elementor_id ) !== 'publish' ) {
104 $settings['mode'] = 'built_in';
105 return $settings;
106 }
107
108 // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Reviewed for the NotificationX codebase: acceptable in this context.
109 $resolved_id = apply_filters( 'wpml_object_id', $elementor_id, 'nx_exit_intent', true );
110 $html = \Elementor\Plugin::$instance->frontend->get_builder_content_for_display( $resolved_id, false );
111
112 // Surface the Layout-panel settings (registered in register_popup_layout_controls)
113 // so the popup runtime can honour them, and set --nx-exit-width on a wrapper so
114 // the section width constraint reflects the configured Width on the frontend.
115 $layout = $this->get_popup_layout_settings( $resolved_id );
116 $wrapper_vars = '';
117 if ( ! empty( $layout['width'] ) ) {
118 $wrapper_vars .= '--nx-exit-width:' . $layout['width'] . ';';
119 }
120 if ( ! empty( $layout['height'] ) ) {
121 $wrapper_vars .= '--nx-exit-height:' . $layout['height'] . ';';
122 }
123 if ( '' !== $wrapper_vars ) {
124 $html = '<div class="nx-exit-intent-elementor-wrap" style="' . esc_attr( $wrapper_vars ) . '">' . $html . '</div>';
125 }
126
127 $settings['mode'] = 'elementor';
128 $settings['elementor_html'] = $html;
129 $settings['popup_layout'] = $layout;
130 return $settings;
131 }
132
133 /**
134 * Read the Layout-panel settings off an nx_exit_intent Elementor document and
135 * normalise them into a flat array the popup runtime can consume.
136 *
137 * @param int $elementor_id
138 * @return array
139 */
140 protected function get_popup_layout_settings( $elementor_id ) {
141 $defaults = [
142 'width' => '',
143 'height_mode' => 'fit',
144 'height' => '',
145 'horizontal' => 'center',
146 'vertical' => 'center',
147 'overlay' => true,
148 'close_button' => true,
149 'entrance_animation' => '',
150 'exit_animation' => '',
151 ];
152
153 if ( ! class_exists( '\\Elementor\\Plugin' ) ) {
154 return $defaults;
155 }
156 $document = \Elementor\Plugin::$instance->documents->get( $elementor_id );
157 if ( ! $document ) {
158 return $defaults;
159 }
160
161 $size = static function ( $value ) {
162 if ( is_array( $value ) && isset( $value['size'] ) && '' !== $value['size'] ) {
163 return $value['size'] . ( isset( $value['unit'] ) ? $value['unit'] : 'px' );
164 }
165 return '';
166 };
167
168 $height_mode = $document->get_settings( 'nx_popup_height' );
169
170 // Width precedence: the Layout-panel Width control if the user set one,
171 // otherwise the design's own top-level container width (boxed_width).
172 // Without this, every popup falls back to the CSS 540px default, which
173 // crushes wider designs (e.g. the two-column 896px themes).
174 $width = $size( $document->get_settings( 'nx_popup_width' ) );
175 if ( '' === $width ) {
176 $width = $this->resolve_design_width( $document );
177 }
178
179 return [
180 'width' => $width,
181 'height_mode' => $height_mode ? $height_mode : 'fit',
182 'height' => 'custom' === $height_mode ? $size( $document->get_settings( 'nx_popup_custom_height' ) ) : '',
183 'horizontal' => $document->get_settings( 'nx_popup_horizontal' ) ?: 'center',
184 'vertical' => $document->get_settings( 'nx_popup_vertical' ) ?: 'center',
185 'overlay' => 'yes' === $document->get_settings( 'nx_popup_overlay' ),
186 'close_button' => 'yes' === $document->get_settings( 'nx_popup_close_button' ),
187 'entrance_animation' => (string) $document->get_settings( 'nx_popup_entrance_animation' ),
188 'exit_animation' => (string) $document->get_settings( 'nx_popup_exit_animation' ),
189 ];
190 }
191
192 /**
193 * Derive the popup's intended width from its Elementor design — the widest
194 * px width among the top-level elements (boxed_width / width / content_width).
195 * Used as the default popup width when no Layout-panel Width is configured,
196 * so each design keeps its own width instead of the 540px CSS fallback.
197 * Only top-level elements are inspected so inner column widths are ignored.
198 *
199 * @param \Elementor\Core\Base\Document $document
200 * @return string e.g. "896px", or '' when no explicit px width is defined.
201 */
202 protected function resolve_design_width( $document ) {
203 if ( ! $document || ! method_exists( $document, 'get_elements_data' ) ) {
204 return '';
205 }
206 $data = $document->get_elements_data();
207 if ( empty( $data ) || ! is_array( $data ) ) {
208 return '';
209 }
210 $max = 0;
211 foreach ( $data as $element ) {
212 if ( empty( $element['settings'] ) || ! is_array( $element['settings'] ) ) {
213 continue;
214 }
215 foreach ( [ 'boxed_width', 'width', 'content_width' ] as $key ) {
216 $val = isset( $element['settings'][ $key ] ) ? $element['settings'][ $key ] : null;
217 if ( is_array( $val ) && isset( $val['size'] ) && is_numeric( $val['size'] )
218 && ( ! isset( $val['unit'] ) || 'px' === $val['unit'] ) ) {
219 $max = max( $max, (float) $val['size'] );
220 }
221 }
222 }
223 return $max > 0 ? $max . 'px' : '';
224 }
225
226 /**
227 * Make sure Elementor's per-document CSS/JS is enqueued on every page where
228 * an Exit Intent campaign can fire — get_builder_content_for_display alone
229 * registers some assets, but not on pages other than the document's own URL.
230 */
231 public function enqueue_elementor_assets() {
232 if ( ! class_exists( '\\Elementor\\Core\\Files\\CSS\\Post' ) ) {
233 return;
234 }
235 $ids = [];
236 try {
237 $posts = \NotificationX\Core\PostType::get_instance()->get_posts( [ 'source' => $this->id ] );
238 foreach ( $posts as $post ) {
239 if ( ! empty( $post['elementor_id'] ) && empty( $post['enabled'] ) === false ) {
240 $ids[] = (int) $post['elementor_id'];
241 }
242 }
243 } catch ( \Exception $e ) {
244 return;
245 }
246 foreach ( $ids as $id ) {
247 try {
248 $css = \Elementor\Core\Files\CSS\Post::create( $id );
249 if ( $css ) {
250 $css->enqueue();
251 }
252 } catch ( \Exception $e ) {
253 // swallow — one broken doc shouldn't break the rest.
254 }
255 }
256 }
257
258 /**
259 * Register the post type that backs Elementor-designed Exit Intent popups.
260 *
261 * Mirrors PressBar's `nx_bar`: not public, but publicly_queryable so Elementor's
262 * preview routes resolve. `supports[]` must include `elementor` for Elementor's
263 * documents API to treat this as a first-class document.
264 *
265 * @return void
266 */
267 public static function register_post_type() {
268 register_post_type( 'nx_exit_intent', [
269 'label' => __( 'NotificationX Exit Intent', 'notificationx' ),
270 'public' => false,
271 'publicly_queryable' => true,
272 'show_ui' => false,
273 'show_in_menu' => true,
274 'show_in_nav_menus' => false,
275 'exclude_from_search' => true,
276 'rewrite' => false,
277 'capability_type' => 'post',
278 'hierarchical' => false,
279 'menu_icon' => 'dashicons-admin-page',
280 'supports' => [ 'title', 'content', 'author', 'elementor' ],
281 ] );
282 }
283
284 /**
285 * Add a "Layout" section to the Elementor document-settings panel, but ONLY
286 * for nx_exit_intent documents. Mirrors Elementor Pro's Popup → Layout panel
287 * (Width / Height / Position / Overlay / Close Button / Animations).
288 *
289 * Hooked on `elementor/documents/register_controls`, which fires for every
290 * document right after its built-in controls register. We bail for any other
291 * post type so these controls never leak into normal pages/posts.
292 *
293 * Width is wired live via the `--nx-exit-width` CSS variable that
294 * print_section_constraint_css()/inject_elementor_html() read; the remaining
295 * controls persist into the document's page settings (`_elementor_page_settings`)
296 * so the popup runtime can consume them.
297 *
298 * @param \Elementor\Core\Base\Document $document
299 * @return void
300 */
301 public function register_popup_layout_controls( $document ) {
302 if ( ! is_object( $document ) || ! method_exists( $document, 'get_post' ) ) {
303 return;
304 }
305 $post = $document->get_post();
306 if ( ! $post || 'nx_exit_intent' !== $post->post_type ) {
307 return;
308 }
309
310 $cm = '\Elementor\Controls_Manager';
311
312 $document->start_controls_section(
313 'nx_popup_layout',
314 [
315 'label' => __( 'Layout', 'notificationx' ),
316 'tab' => $cm::TAB_SETTINGS,
317 ]
318 );
319
320 $document->add_responsive_control(
321 'nx_popup_width',
322 [
323 'label' => __( 'Width', 'notificationx' ),
324 'type' => $cm::SLIDER,
325 'size_units' => [ 'px', '%', 'vw' ],
326 'range' => [
327 'px' => [ 'min' => 100, 'max' => 1140 ],
328 '%' => [ 'min' => 10, 'max' => 100 ],
329 'vw' => [ 'min' => 10, 'max' => 100 ],
330 ],
331 // No default: when the user hasn't set a Width, the popup falls back
332 // to the design's own container width (resolve_design_width()), not a
333 // fixed 540px — otherwise wider designs (e.g. the 896px two-column
334 // themes) get crushed. An explicit value here still wins.
335 //
336 // NOTE: no `selectors` key — Elementor silently drops document-level
337 // controls that carry `selectors`. The value is applied server-side
338 // via the --nx-exit-width variable in print_section_constraint_css()
339 // (preview) and inject_elementor_html() (frontend) instead.
340 'default' => [ 'unit' => 'px', 'size' => '' ],
341 ]
342 );
343
344 $document->add_control(
345 'nx_popup_height',
346 [
347 'label' => __( 'Height', 'notificationx' ),
348 'type' => $cm::SELECT,
349 'default' => 'fit',
350 'options' => [
351 'fit' => __( 'Fit To Content', 'notificationx' ),
352 'custom' => __( 'Custom Height', 'notificationx' ),
353 ],
354 ]
355 );
356
357 $document->add_responsive_control(
358 'nx_popup_custom_height',
359 [
360 'label' => __( 'Custom Height', 'notificationx' ),
361 'type' => $cm::SLIDER,
362 'size_units' => [ 'px', 'vh' ],
363 'range' => [
364 'px' => [ 'min' => 100, 'max' => 1000 ],
365 'vh' => [ 'min' => 10, 'max' => 100 ],
366 ],
367 'default' => [ 'unit' => 'px', 'size' => 480 ],
368 'condition' => [ 'nx_popup_height' => 'custom' ],
369 // No `selectors` (see nx_popup_width note); applied via --nx-exit-height.
370 ]
371 );
372
373 $document->add_control(
374 'nx_popup_position_heading',
375 [
376 'label' => __( 'Position', 'notificationx' ),
377 'type' => $cm::HEADING,
378 'separator' => 'before',
379 ]
380 );
381
382 $document->add_responsive_control(
383 'nx_popup_horizontal',
384 [
385 'label' => __( 'Horizontal', 'notificationx' ),
386 'type' => $cm::CHOOSE,
387 'default' => 'center',
388 'options' => [
389 'start' => [ 'title' => __( 'Start', 'notificationx' ), 'icon' => 'eicon-h-align-left' ],
390 'center' => [ 'title' => __( 'Center', 'notificationx' ), 'icon' => 'eicon-h-align-center' ],
391 'end' => [ 'title' => __( 'End', 'notificationx' ), 'icon' => 'eicon-h-align-right' ],
392 ],
393 ]
394 );
395
396 $document->add_responsive_control(
397 'nx_popup_vertical',
398 [
399 'label' => __( 'Vertical', 'notificationx' ),
400 'type' => $cm::CHOOSE,
401 'default' => 'center',
402 'options' => [
403 'start' => [ 'title' => __( 'Top', 'notificationx' ), 'icon' => 'eicon-v-align-top' ],
404 'center' => [ 'title' => __( 'Middle', 'notificationx' ), 'icon' => 'eicon-v-align-middle' ],
405 'end' => [ 'title' => __( 'Bottom', 'notificationx' ), 'icon' => 'eicon-v-align-bottom' ],
406 ],
407 ]
408 );
409
410 $document->add_control(
411 'nx_popup_overlay',
412 [
413 'label' => __( 'Overlay', 'notificationx' ),
414 'type' => $cm::SWITCHER,
415 'label_on' => __( 'Show', 'notificationx' ),
416 'label_off' => __( 'Hide', 'notificationx' ),
417 'return_value' => 'yes',
418 'default' => 'yes',
419 'separator' => 'before',
420 ]
421 );
422
423 $document->add_control(
424 'nx_popup_close_button',
425 [
426 'label' => __( 'Close Button', 'notificationx' ),
427 'type' => $cm::SWITCHER,
428 'label_on' => __( 'Show', 'notificationx' ),
429 'label_off' => __( 'Hide', 'notificationx' ),
430 'return_value' => 'yes',
431 'default' => 'yes',
432 ]
433 );
434
435 $document->add_control(
436 'nx_popup_entrance_animation',
437 [
438 'label' => __( 'Entrance Animation', 'notificationx' ),
439 'type' => $cm::ANIMATION,
440 'separator' => 'before',
441 ]
442 );
443
444 $document->add_control(
445 'nx_popup_exit_animation',
446 [
447 'label' => __( 'Exit Animation', 'notificationx' ),
448 'type' => $cm::EXIT_ANIMATION,
449 ]
450 );
451
452 $document->end_controls_section();
453 }
454
455 /**
456 * Live-preview bridge for the Layout panel inside the Elementor editor.
457 *
458 * Document-level controls can't carry a `selectors` key (Elementor drops
459 * them), so Width/Height don't get Elementor's built-in live CSS injection.
460 * This registers page-settings change callbacks that write the
461 * `--nx-exit-width` / `--nx-exit-height` variables straight onto the preview
462 * iframe's <body> as the slider moves — so changes reflect instantly instead
463 * of only after a reload.
464 *
465 * Scoped to nx_exit_intent documents only.
466 *
467 * @return void
468 */
469 public function enqueue_editor_live_preview() {
470 if ( ! class_exists( '\\Elementor\\Plugin' ) ) {
471 return;
472 }
473 $post_id = \Elementor\Plugin::$instance->editor->get_post_id();
474 if ( ! $post_id || 'nx_exit_intent' !== get_post_type( $post_id ) ) {
475 return;
476 }
477
478 $js = <<<'JS'
479 ( function ( $ ) {
480 function toCss( value ) {
481 if ( value && '' !== value.size && null != value.size ) {
482 return value.size + ( value.unit || 'px' );
483 }
484 return '';
485 }
486 function previewBody() {
487 var frame = elementor.$preview && elementor.$preview[0];
488 var win = frame && frame.contentWindow;
489 return win && win.document ? win.document.body : null;
490 }
491 function applyVar( name, value ) {
492 var body = previewBody();
493 if ( ! body ) { return; }
494 var css = toCss( value );
495 if ( css ) { body.style.setProperty( name, css ); }
496 else { body.style.removeProperty( name ); }
497 }
498 $( window ).on( 'elementor:init', function () {
499 if ( 'undefined' === typeof elementor || ! elementor.settings || ! elementor.settings.page ) {
500 return;
501 }
502 elementor.settings.page.addChangeCallback( 'nx_popup_width', function ( value ) {
503 applyVar( '--nx-exit-width', value );
504 } );
505 elementor.settings.page.addChangeCallback( 'nx_popup_custom_height', function ( value ) {
506 applyVar( '--nx-exit-height', value );
507 } );
508 } );
509 } )( jQuery );
510 JS;
511
512 wp_add_inline_script( 'elementor-editor', $js );
513 }
514
515 /**
516 * Create a new `nx_exit_intent` Elementor document from a seed theme and
517 * respond to the admin with the new post ID + Elementor edit URL.
518 *
519 * Counterpart of PressBar::create_bar_of_type_bar_with_elementor().
520 *
521 * @param array $params { theme_id: string }
522 * @return void Always responds via wp_send_json_{success,error}.
523 */
524 public function create_exit_intent_with_elementor( $params ) {
525 if ( empty( $params['theme_id'] ) ) {
526 wp_send_json_error( 'missing_theme_id' );
527 }
528
529 $theme = sanitize_text_field( $params['theme_id'] );
530 $importer = new Importer();
531
532 $ID = $importer->create_nx( [
533 'theme' => $theme,
534 'post_title' => 'Design for NotificationX Exit Intent - ',
535 ] );
536
537 if ( $ID && ! is_wp_error( $ID ) ) {
538 // Strip theme chrome — popup HTML inlines into the host page.
539 update_post_meta( $ID, '_wp_page_template', 'elementor_canvas' );
540 wp_send_json_success( [
541 'context' => [
542 'themes' => null,
543 'elementor_id' => $ID,
544 'elementor_edit_link' => \Elementor\Plugin::$instance->documents->get( $ID )->get_edit_url(),
545 ],
546 ] );
547 } else {
548 wp_send_json_error( is_wp_error( $ID ) ? $ID->get_error_message() : 'failed' );
549 }
550 }
551
552 /**
553 * Delete the linked `nx_exit_intent` Elementor post (and its WPML translations).
554 *
555 * @param int|string $elementor_id
556 * @return void
557 */
558 public function delete_elementor_post( $elementor_id ) {
559 if ( empty( $elementor_id ) ) {
560 return;
561 }
562 // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Reviewed for the NotificationX codebase: acceptable in this context.
563 $languages = apply_filters( 'wpml_active_languages', null );
564 if ( is_array( $languages ) ) {
565 foreach ( $languages as $lang => $val ) {
566 // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Reviewed for the NotificationX codebase: acceptable in this context.
567 $translated_id = apply_filters( 'wpml_object_id', $elementor_id, 'nx_exit_intent', false, $lang );
568 if ( $translated_id ) {
569 Helper::delete_owned_post( $translated_id, 'nx_exit_intent' );
570 }
571 }
572 return;
573 }
574 Helper::delete_owned_post( $elementor_id, 'nx_exit_intent' );
575 }
576
577 /**
578 * Redirect WP "Edit" links for nx_exit_intent posts straight into the Elementor editor.
579 *
580 * @param string $link
581 * @param int $id
582 * @param string $context
583 * @return string
584 */
585 public function filter_edit_post_link( $link, $id, $context ) {
586 $post = get_post( $id );
587 if ( $post && 'nx_exit_intent' === $post->post_type && class_exists( '\\Elementor\\Plugin' ) ) {
588 return \Elementor\Plugin::$instance->documents->get( $id )->get_edit_url();
589 }
590 return $link;
591 }
592
593 public function init_extension() {
594 $this->title = __( 'Exit Intent Popup', 'notificationx' );
595 $this->module_title = __( 'Exit Intent Popup', 'notificationx' );
596 $this->themes = [
597 'theme-one' => [
598 'source' => NOTIFICATIONX_ADMIN_URL . 'images/extensions/themes/exit-intent/exit-intent-theme-one.png',
599 'defaults' => [
600 'exit_intent_title' => __( 'Wait! Before You Go……', 'notificationx' ),
601 'exit_intent_subtitle' => __( "We'd love to get your feedback and help us make this better for everyone.", 'notificationx' ),
602 'exit_intent_button_text' => __( 'SUBMIT', 'notificationx' ),
603 // 'exit_intent_show_name' => true,
604 // 'exit_intent_show_email' => true,
605 'exit_intent_name_label' => __( 'Name *', 'notificationx' ),
606 'exit_intent_email_label' => __( 'Enter Your Email *', 'notificationx' ),
607 // 'exit_intent_show_message' => false,
608 'exit_intent_message_placeholder' => __( 'Your message...', 'notificationx' ),
609 'position' => 'center',
610 ],
611 'column' => '5',
612 ],
613 'theme-four' => [
614 'source' => NOTIFICATIONX_ADMIN_URL . 'images/extensions/themes/exit-intent/exit-intent-theme-four.png',
615 'defaults' => [
616 'exit_intent_t4_badge' => __( 'Before you go...', 'notificationx' ),
617 'exit_intent_t4_title' => __( 'Watch this short demo video', 'notificationx' ),
618 'exit_intent_t4_subtitle' => __( 'See how our product simplifies your workflow.', 'notificationx' ),
619 'exit_intent_image_url' => [ 'url' => 'https://notificationx.com/wp-content/uploads/2026/05/exit-intend-theme-four.jpg' ],
620 'exit_intent_t4_video_url' => '',
621 'position' => 'center',
622 ],
623 'column' => '5',
624 ],
625 'theme-three' => [
626 'source' => NOTIFICATIONX_ADMIN_URL . 'images/extensions/themes/exit-intent/exit-intent-theme-three.png',
627 'defaults' => [
628 'exit_intent_t3_title' => __( "Wait, don't go!", 'notificationx' ),
629 'exit_intent_t3_subtitle' => __( 'Before you leave, we have a special offer just for you!', 'notificationx' ),
630 'exit_intent_t3_offer' => __( 'Get 15% off your next purchase!', 'notificationx' ),
631 'exit_intent_t3_coupon_text' => __( "Use code STAY15 at checkout. Don't miss out on this limited-time offer.", 'notificationx' ),
632 'exit_intent_button_text' => __( 'Claim Offer', 'notificationx' ),
633 'exit_intent_dismiss_text' => __( 'No, thanks!', 'notificationx' ),
634 'exit_intent_image_url' => [ 'url' => 'https://notificationx.com/wp-content/uploads/2026/05/exit-intend-theme-three.png' ],
635 'position' => 'center',
636 ],
637 'column' => '5',
638 ],
639 'theme-six' => [
640 'source' => NOTIFICATIONX_ADMIN_URL . 'images/extensions/themes/exit-intent/exit-intent-theme-seven.png',
641 'defaults' => [
642 'exit_intent_t6_title' => __( 'Limited Edition Bass Boost Headphones', 'notificationx' ),
643 // 'exit_intent_t6_show_timer' => true,
644 'exit_intent_t6_countdown_label' => __( 'Offer Ends In', 'notificationx' ),
645 'exit_intent_countdown_end' => '',
646 'exit_intent_t6_days_label' => __( 'DAYS', 'notificationx' ),
647 'exit_intent_t6_hours_label' => __( 'HOURS', 'notificationx' ),
648 'exit_intent_t6_minutes_label' => __( 'MIN', 'notificationx' ),
649 'exit_intent_t6_seconds_label' => __( 'SEC', 'notificationx' ),
650 'exit_intent_button_text' => __( 'Grab Now', 'notificationx' ),
651 'exit_intent_image_url' => [ 'url' => 'https://notificationx.com/wp-content/uploads/2026/05/exit-intend-theme-six.png' ],
652 'position' => 'center',
653 ],
654 'column' => '5',
655 'is_pro' => true,
656 ],
657 'theme-two' => [
658 'source' => NOTIFICATIONX_ADMIN_URL . 'images/extensions/themes/exit-intent/exit-intent-theme-two.png',
659 'defaults' => [
660 'exit_intent_sale_badge' => __( 'Flash Sale', 'notificationx' ),
661 'exit_intent_sale_headline' => __( '50% OFF', 'notificationx' ),
662 'exit_intent_sale_desc' => __( 'ON ENTIRE ORDER', 'notificationx' ),
663 'exit_intent_button_text' => __( 'Shop The Flash Sale Now', 'notificationx' ),
664 'exit_intent_dismiss_text' => __( 'NO, THANKS!', 'notificationx' ),
665 'exit_intent_image_url' => [ 'url' => 'https://notificationx.com/wp-content/uploads/2026/05/exit-intend-theme-two.jpg' ],
666 'position' => 'center',
667 ],
668 'column' => '5',
669 'is_pro' => true,
670 ],
671 'theme-seven' => [
672 'source' => NOTIFICATIONX_ADMIN_URL . 'images/extensions/themes/exit-intent/exit-intent-theme-six.png',
673 'defaults' => [
674 'exit_intent_t7_headline' => __( 'Turn Your House Into a Home', 'notificationx' ),
675 'exit_intent_t7_discount_text' => __( 'Your First Order Comes With a Surprise Deal!', 'notificationx' ),
676 'exit_intent_t7_description' => __( 'Handpicked decor that feels like home the moment it arrives.', 'notificationx' ),
677 'exit_intent_t7_email_placeholder' => __( 'Enter your email', 'notificationx' ),
678 'exit_intent_button_text' => __( 'SEND COUPON', 'notificationx' ),
679 'exit_intent_image_url' => [ 'url' => 'https://notificationx.com/wp-content/uploads/2026/05/exit-intend-theme-five.png' ],
680 'position' => 'center',
681 ],
682 'column' => '5',
683 'is_pro' => true,
684 ],
685 'theme-five' => [
686 'source' => NOTIFICATIONX_ADMIN_URL . 'images/extensions/themes/exit-intent/exit-intent-theme-five.png',
687 'defaults' => [
688 'exit_intent_t5_title' => __( 'Flash Sale', 'notificationx' ),
689 'exit_intent_t5_headline' => __( '50% OFF', 'notificationx' ),
690 'exit_intent_t5_desc' => __( 'ON ENTIRE ORDER', 'notificationx' ),
691 'exit_intent_t5_countdown_label' => __( 'LIMITED-TIME OFFER! SALE ENDS IN', 'notificationx' ),
692 'exit_intent_countdown_end' => '',
693 'exit_intent_t5_days_label' => __( 'DAYS', 'notificationx' ),
694 'exit_intent_t5_hours_label' => __( 'HRS', 'notificationx' ),
695 'exit_intent_t5_minutes_label' => __( 'MIN', 'notificationx' ),
696 'exit_intent_t5_seconds_label' => __( 'SEC', 'notificationx' ),
697 'exit_intent_t5_timer_bg' => '#fff0f5',
698 'exit_intent_t5_timer_color' => '#e91e63',
699 'exit_intent_button_text' => __( 'Shop The Flash Sale Now', 'notificationx' ),
700 'exit_intent_dismiss_text' => __( 'NO, THANKS!', 'notificationx' ),
701 'exit_intent_image_url' => [ 'url' => 'https://notificationx.com/wp-content/uploads/2026/05/exit-intend-theme-six.jpg' ],
702 'position' => 'center',
703 ],
704 'column' => '5',
705 'is_pro' => true,
706 ],
707 ];
708
709 // Elementor seed themes for the "Build With Elementor" modal.
710 // Each entry has a matching JSON file under jsons/{value}.json (Task 07).
711 // Preview images live under assets/admin/images/extensions/themes/exit-intent-elementor/.
712 // Until per-theme Elementor previews are rendered, we fall back to the existing
713 // built-in screenshots so the modal renders with a real image.
714 // Order intentionally mirrors the built-in $themes preset order above
715 // (one, four, three, six, two, seven, five) so the Elementor modal lists
716 // the same designs in the same sequence as the Default-theme picker.
717 //
718 // Unlike $themes (processed by Extension::__nx_themes), this array is fed
719 // straight to the radio-card field, so we must resolve the pro badge here:
720 // only flag a theme as pro when the Pro plugin is NOT active.
721 $is_pro_badge = ! NotificationX::is_pro();
722 $this->elementor_themes = [
723 'theme-one' => [
724 'label' => 'theme-one',
725 'value' => 'theme-one',
726 'icon' => NOTIFICATIONX_ADMIN_URL . 'images/extensions/themes/exit-intent/exit-intent-theme-one.png',
727 'column' => '6',
728 'title' => 'Exit Intent Theme One',
729 ],
730 'theme-four' => [
731 'label' => 'theme-four',
732 'value' => 'theme-four',
733 'icon' => NOTIFICATIONX_ADMIN_URL . 'images/extensions/themes/exit-intent/exit-intent-theme-four.png',
734 'column' => '6',
735 'title' => 'Exit Intent Theme Four',
736 ],
737 'theme-three' => [
738 'label' => 'theme-three',
739 'value' => 'theme-three',
740 'icon' => NOTIFICATIONX_ADMIN_URL . 'images/extensions/themes/exit-intent/exit-intent-theme-three.png',
741 'column' => '6',
742 'title' => 'Exit Intent Theme Three',
743 ],
744 'theme-six' => [
745 'label' => 'theme-six',
746 'value' => 'theme-six',
747 'icon' => NOTIFICATIONX_ADMIN_URL . 'images/extensions/themes/exit-intent/exit-intent-theme-six.png',
748 'column' => '6',
749 'title' => 'Exit Intent Theme Six',
750 'is_pro' => $is_pro_badge,
751 ],
752 'theme-two' => [
753 'label' => 'theme-two',
754 'value' => 'theme-two',
755 'icon' => NOTIFICATIONX_ADMIN_URL . 'images/extensions/themes/exit-intent/exit-intent-theme-two.png',
756 'column' => '6',
757 'title' => 'Exit Intent Theme Two',
758 'is_pro' => $is_pro_badge,
759 ],
760 'theme-seven' => [
761 'label' => 'theme-seven',
762 'value' => 'theme-seven',
763 'icon' => NOTIFICATIONX_ADMIN_URL . 'images/extensions/themes/exit-intent/exit-intent-theme-seven.png',
764 'column' => '6',
765 'title' => 'Exit Intent Theme Seven',
766 'is_pro' => $is_pro_badge,
767 ],
768 'theme-five' => [
769 'label' => 'theme-five',
770 'value' => 'theme-five',
771 'icon' => NOTIFICATIONX_ADMIN_URL . 'images/extensions/themes/exit-intent/exit-intent-theme-five.png',
772 'column' => '6',
773 'title' => 'Exit Intent Theme Five',
774 'is_pro' => $is_pro_badge,
775 ],
776 ];
777 }
778
779 public function init_fields() {
780 parent::init_fields();
781 add_filter( 'nx_content_fields', [ $this, 'content_fields' ], 999 );
782 add_filter( 'nx_design_tab_fields', [ $this, 'design_fields' ], 99 );
783 add_filter( 'nx_design_tab_fields', [ $this, 'builder_tabs_fields' ], 100 );
784 add_filter( 'nx_customize_fields', [ $this, 'customize_fields' ], 999 );
785 add_filter( 'nx_display_fields', [ $this, 'display_fields' ], 999 );
786
787 // Hide per-theme content sections when an Elementor doc is linked (Task 06).
788 // We deliberately do NOT hide the themes radio-card itself — keeping it
789 // visible on the Default tab lets users switch back to a built-in theme
790 // after they've imported an Elementor design.
791 add_filter( 'nx_content_fields', [ $this, 'suppress_when_elementor' ], 1001 );
792
793 // Hide the whole Content wizard step when an Elementor design is linked —
794 // suppress_when_elementor() empties every content section in that case, so
795 // the step would otherwise render as a blank tab.
796 add_filter( 'nx_metabox_tabs', [ $this, 'hide_content_tab_for_elementor' ] );
797 }
798
799 /**
800 * Remove the Content step from the wizard for Exit Intent campaigns that are
801 * built with Elementor. Elementor owns all of the popup's content there (every
802 * `exit_intent_*_section` is suppressed by suppress_when_elementor()), leaving
803 * the Content tab empty. Built-in themes keep the Content step, and all other
804 * sources are untouched.
805 *
806 * The tab stays visible UNLESS (source is Exit Intent AND elementor_id is a
807 * number), expressed below as the OR'd "show when" rule.
808 */
809 public function hide_content_tab_for_elementor( $tabs ) {
810 if ( empty( $tabs['content_tab'] ) ) {
811 return $tabs;
812 }
813 $show_rule = Rules::_logicalRule( [
814 Rules::_is( 'source', $this->id, true ),
815 Rules::_isOfType( 'elementor_id', 'number', true ),
816 ], 'or' );
817 $tabs['content_tab'] = Rules::add( $show_rule, $tabs['content_tab'] );
818 return $tabs;
819 }
820
821 /**
822 * Hide every per-theme content section once an Elementor design is linked.
823 * Elementor owns all of these fields; showing them would let users edit
824 * values with no visible effect on the rendered popup.
825 */
826 public function suppress_when_elementor( $fields ) {
827 foreach ( $fields as $key => $field ) {
828 if ( strpos( $key, 'exit_intent_' ) === 0 && substr( $key, -8 ) === '_section' ) {
829 $fields[ $key ] = Rules::isOfType( 'elementor_id', 'number', true, $field );
830 }
831 }
832 return $fields;
833 }
834
835 /**
836 * @deprecated Kept for back-compat only; the filter that called this has
837 * been removed so the themes radio stays visible on the Default tab even
838 * when an Elementor design is linked (lets users switch back).
839 */
840 public function suppress_themes_radio( $fields ) {
841 if ( ! isset( $fields['themes']['fields']['themes']['rules'] ) ) {
842 return $fields;
843 }
844 $existing = $fields['themes']['fields']['themes']['rules'];
845 // Additional clause: either we're NOT on Exit Intent (so this rule
846 // shouldn't change anything for other sources), OR elementor_id is
847 // not yet set. Combined via AND with the existing tab-gate so other
848 // sources (e.g. PressBar) keep their tab-based hiding intact.
849 $additional = Rules::logicalRule( [
850 Rules::is( 'source', $this->id, true ),
851 Rules::isOfType( 'elementor_id', 'number', true ),
852 ], 'or' );
853 $fields['themes']['fields']['themes']['rules'] = Rules::logicalRule( [
854 $existing,
855 $additional,
856 ], 'and' );
857 return $fields;
858 }
859
860 /**
861 * Restructures the Themes section into two tabs:
862 * 1. Default — original 7 React themes grid (the `themes` radio-card,
863 * already gated to themes_tab='for_desktop' in GlobalFields).
864 * 2. Custom — Build With Elementor button + modal, plus Edit/Remove
865 * and Install fallback after a doc is linked.
866 *
867 * Matches Notification Bar's Default/Custom tab structure.
868 */
869 public function builder_tabs_fields( $fields ) {
870 if ( empty( $fields['themes']['fields']['themes_section']['fields']['themes_tab']['fields'] ) ) {
871 return $fields;
872 }
873 $tab =& $fields['themes']['fields']['themes_section']['fields']['themes_tab']['fields'];
874
875 // Hide the global "For Desktop" / "For Mobile" tabs for Exit Intent only — we
876 // ship our own Default + Custom tabs below.
877 foreach ( [ 'for_desktop', 'for_mobile' ] as $key ) {
878 if ( isset( $tab[ $key ] ) ) {
879 $tab[ $key ] = Rules::is( 'source', $this->id, true, $tab[ $key ] );
880 }
881 }
882
883 // Expand the global themes radio-card so it ALSO renders when our new
884 // Default tab is active. Without this, the radio would never appear for
885 // Exit Intent — its existing rule is hard-coded to themes_tab='for_desktop'.
886 if ( isset( $fields['themes']['fields']['themes'] ) ) {
887 $fields['themes']['fields']['themes']['rules'] = Rules::logicalRule( [
888 Rules::is( 'themes_tab', 'for_desktop' ),
889 Rules::is( 'themes_tab', 'exit_intent_default_tab' ),
890 ], 'or' );
891 }
892
893 $is_installed = Helper::is_plugin_installed( 'elementor/elementor.php' );
894 $install_activate_text = $is_installed
895 ? __( 'Activate Elementor', 'notificationx' )
896 : __( 'Install Elementor', 'notificationx' );
897
898 // Default tab — unique name (no collision with global for_desktop).
899 $tab[] = [
900 'label' => __( 'Default', 'notificationx' ),
901 'name' => 'exit_intent_default_tab',
902 'id' => 'exit_intent_default_tab',
903 'type' => 'section',
904 'icon' => NOTIFICATIONX_ADMIN_URL . 'images/icons/nxbar-presets-icon.svg',
905 'rules' => Rules::is( 'source', $this->id ),
906 ];
907
908 // The Custom tab — only renders for Exit Intent campaigns.
909 $tab[] = [
910 'label' => __( 'Custom', 'notificationx' ),
911 'name' => 'exit_intent_custom_tab',
912 'id' => 'exit_intent_custom_tab',
913 'type' => 'section',
914 'icon' => NOTIFICATIONX_ADMIN_URL . 'images/icons/nxbar-custom-tab.svg',
915 'rules' => Rules::is( 'source', $this->id ),
916 'fields' => [
917
918 // Preview image / empty-state illustration (reuses NxBarPresets).
919 'exit_intent_custom_preview' => [
920 'name' => 'exit_intent_custom_preview',
921 'type' => 'exit-intent-custom',
922 'label' => __( 'Exit Intent', 'notificationx' ),
923 'priority' => 5,
924 ],
925
926 // Edit With Elementor — shown when an Elementor doc is linked.
927 'elementor_edit_link' => [
928 'name' => 'elementor_edit_link',
929 'type' => 'button',
930 'text' => __( 'Edit With Elementor', 'notificationx' ),
931 'href' => -1,
932 'priority' => 10,
933 'target' => '_blank',
934 'rules' => Rules::logicalRule( [
935 Rules::is( 'elementor_edit_link', false, true ),
936 Rules::isOfType( 'elementor_edit_link', 'string' ),
937 Rules::is( 'elementor_id', false, true ),
938 ] ),
939 ],
940
941 // Remove — deletes the Elementor doc and resets state.
942 'nx-exit-intent_with_elementor-remove' => [
943 'name' => 'nx-exit-intent_with_elementor-remove',
944 'type' => 'button',
945 'text' => __( 'Remove', 'notificationx' ),
946 'priority' => 15,
947 'rules' => Rules::logicalRule( [
948 Rules::is( 'elementor_id', false, true ),
949 Rules::is( 'is_elementor', true ),
950 Rules::is( 'source', $this->id ),
951 ] ),
952 'ajax' => [
953 'on' => 'click',
954 'api' => '/notificationx/v1/exit-intent/elementor/remove',
955 'data' => [ 'elementor_id' => '@elementor_id' ],
956 'hideSwal' => true,
957 ],
958 'trigger' => [
959 [
960 'type' => 'setFieldValue',
961 'action' => [
962 'elementor_id' => false,
963 'elementor_edit_link' => '',
964 'elementor_exit_theme' => 'theme-one',
965 'is_confirmed' => false,
966 'themes' => $this->id . '_theme-one',
967 ],
968 ],
969 ],
970 ],
971
972 // Build With Elementor — modal with seed-theme picker.
973 'nx-exit-intent_with_elementor' => [
974 'name' => 'nx-exit-intent_with_elementor',
975 'type' => 'modal',
976 'priority' => 20,
977 'button' => [
978 'name' => 'build_with_elementor',
979 'text' => __( 'Build With Elementor', 'notificationx' ),
980 'trigger' => [
981 [
982 'type' => 'setFieldValue',
983 'action' => [ 'import_elementor_theme' => false ],
984 ],
985 ],
986 ],
987 'confirm_button' => [
988 'type' => 'button',
989 'name' => 'import_elementor_theme',
990 'group' => true,
991 'fields' => [
992 [
993 'type' => 'button',
994 'name' => 'import_elementor_theme',
995 'default' => false,
996 'text' => [
997 'normal' => __( 'Import', 'notificationx' ),
998 'saved' => __( 'Import', 'notificationx' ),
999 'loading' => __( 'Importing...', 'notificationx' ),
1000 ],
1001 'ajax' => [
1002 'on' => 'click',
1003 'api' => '/notificationx/v1/exit-intent/elementor/import',
1004 'data' => [ 'theme_id' => '@elementor_exit_theme' ],
1005 'trigger' => '@is_confirmed:true',
1006 'hideSwal' => true,
1007 ],
1008 'rules' => Rules::is( 'is_confirmed', true, true ),
1009 ],
1010 [
1011 'type' => 'button',
1012 'name' => 'import_elementor_theme_next',
1013 'default' => false,
1014 'text' => __( 'Next', 'notificationx' ),
1015 'rules' => Rules::is( 'is_confirmed', true ),
1016 'trigger' => [
1017 [
1018 'type' => 'setContext',
1019 'action' => [ 'config.active' => 'display_tab' ],
1020 ],
1021 [
1022 'type' => 'setFieldValue',
1023 'action' => [ 'import_elementor_theme_next' => true ],
1024 ],
1025 ],
1026 ],
1027 ],
1028 ],
1029 'cancel' => 'import_elementor_theme_next',
1030 'body' => [
1031 'header' => __( 'Choose Your ', 'notificationx' ),
1032 'fields' => [
1033 'themes' => [
1034 'type' => 'radio-card',
1035 'name' => 'elementor_exit_theme',
1036 'style' => [ 'label' => [ 'position' => 'top' ] ],
1037 'rules' => Rules::is( 'is_confirmed', true, true ),
1038 'default' => 'theme-one',
1039 'options' => $this->elementor_themes,
1040 ],
1041 ],
1042 ],
1043 'rules' => Rules::logicalRule( [
1044 Rules::is( 'elementor_id', false ),
1045 Rules::is( 'is_elementor', true ),
1046 Rules::is( 'is_confirmed', true, true ),
1047 Rules::is( 'source', $this->id ),
1048 ] ),
1049 ],
1050
1051 // Install / Activate Elementor — fallback when Elementor isn't active.
1052 'nx-exit-intent_with_elementor_install' => [
1053 'name' => 'nx-exit-intent_with_elementor_install',
1054 'type' => 'button',
1055 'priority' => 25,
1056 'text' => [
1057 'normal' => $install_activate_text,
1058 'saved' => $is_installed ? __( 'Activated Elementor', 'notificationx' ) : __( 'Installed Elementor', 'notificationx' ),
1059 'loading' => $is_installed ? __( 'Activating Elementor...', 'notificationx' ) : __( 'Installing Elementor...', 'notificationx' ),
1060 ],
1061 'rules' => Rules::logicalRule( [
1062 Rules::is( 'is_elementor', false ),
1063 Rules::is( 'source', $this->id ),
1064 ] ),
1065 'ajax' => [
1066 'on' => 'click',
1067 'api' => '/notificationx/v1/core-install',
1068 'data' => [
1069 'source' => $this->id,
1070 'slug' => 'elementor',
1071 'file' => 'elementor.php',
1072 'is_installed' => $is_installed,
1073 ],
1074 'swal' => [
1075 'icon' => 'success',
1076 'text' => __( 'Successfully Activated', 'notificationx' ),
1077 ],
1078 'trigger' => '@is_elementor:true',
1079 ],
1080 ],
1081
1082 // Hidden state fields (Task 05).
1083 'is_elementor' => [
1084 'name' => 'is_elementor',
1085 'type' => 'hidden',
1086 'default' => class_exists( '\\Elementor\\Plugin' ),
1087 'rules' => Rules::is( 'source', $this->id ),
1088 ],
1089 'elementor_id' => [
1090 'name' => 'elementor_id',
1091 'type' => 'hidden',
1092 'default' => false,
1093 'rules' => Rules::is( 'source', $this->id ),
1094 ],
1095 'is_confirmed' => [
1096 'name' => 'is_confirmed',
1097 'type' => 'hidden',
1098 'default' => false,
1099 ],
1100 ],
1101 ];
1102
1103 // Build With AI tab — Pro feature. Free plugin only registers the tab
1104 // shell + field type; the Pro plugin supplies the actual builder via
1105 // the `nx_build_ai_render` filter (see PressBar's `nxbar_build_with_ai`
1106 // for the same pattern already shipped for Notification Bar).
1107 $tab[] = [
1108 'label' => __( 'Build With AI', 'notificationx' ),
1109 'name' => 'exit_intent_ai_tab',
1110 'id' => 'exit_intent_ai_tab',
1111 'type' => 'section',
1112 'icon' => NOTIFICATIONX_ADMIN_URL . 'images/icons/build-with-ai-icon.svg',
1113 'rules' => Rules::is( 'source', $this->id ),
1114 'fields' => [
1115 'exit_intent_ai_tab' => [
1116 'label' => __( 'Build With AI', 'notificationx' ),
1117 'name' => 'exit_intent_ai_tab',
1118 'id' => 'exit_intent_ai_tab',
1119 'type' => 'section',
1120 'icon' => NOTIFICATIONX_ADMIN_URL . 'images/responsive/desktop.svg',
1121 'fields' => [
1122 'exit_intent_ai_tab_fields' => [
1123 'name' => 'exit_intent_ai_tab_fields',
1124 'type' => 'exit-intent-build_with_ai',
1125 'label' => __( 'Exit Intent', 'notificationx' ),
1126 'priority' => 10,
1127 ],
1128 ],
1129 ],
1130 ],
1131 ];
1132
1133 return $fields;
1134 }
1135
1136 public function display_fields( $fields ) {
1137 if ( isset( $fields['image-section'] ) ) {
1138 $fields['image-section'] = Rules::is( 'source', $this->id, true, $fields['image-section'] );
1139 }
1140 return $fields;
1141 }
1142
1143 public function content_fields( $fields ) {
1144 // Hide standard content fields for this source
1145 if ( isset( $fields['utm_options'] ) ) {
1146 $fields['utm_options'] = Rules::is( 'source', $this->id, true, $fields['utm_options'] );
1147 }
1148 if ( isset( $fields['content'] ) ) {
1149 $fields['content'] = Rules::is( 'source', $this->id, true, $fields['content'] );
1150 }
1151 if ( isset( $fields['link_options'] ) ) {
1152 $fields['link_options'] = Rules::is( 'source', $this->id, true, $fields['link_options'] );
1153 }
1154
1155 // ── Theme Two content fields ─────────────────────────────────────────────
1156 $fields['exit_intent_theme_two_section'] = [
1157 'label' => __( 'Exit Intent Content', 'notificationx' ),
1158 'name' => 'exit_intent_theme_two_section',
1159 'type' => 'section',
1160 'priority' => 5,
1161 'rules' => Rules::logicalRule( [
1162 Rules::is( 'source', $this->id ),
1163 Rules::is( 'themes', $this->id . '_theme-two' ),
1164 ] ),
1165 'fields' => [
1166 [
1167 'label' => __( 'Right Panel Image', 'notificationx' ),
1168 'name' => 'exit_intent_image_url',
1169 'type' => 'media',
1170 'priority' => 5,
1171 'default' => [
1172 'url' => 'https://notificationx.com/wp-content/uploads/2026/05/exit-intend-theme-two-five-seven.jpg',
1173 ],
1174 'help' => __( 'Upload or select the image to display in the right panel.', 'notificationx' ),
1175 ],
1176 [
1177 'label' => __( 'Sale Badge Text', 'notificationx' ),
1178 'name' => 'exit_intent_sale_badge',
1179 'type' => 'text',
1180 'priority' => 10,
1181 'default' => __( 'Flash Sale', 'notificationx' ),
1182 ],
1183 [
1184 'label' => __( 'Sale Headline', 'notificationx' ),
1185 'name' => 'exit_intent_sale_headline',
1186 'type' => 'text',
1187 'priority' => 20,
1188 'default' => __( '50% OFF', 'notificationx' ),
1189 ],
1190 [
1191 'label' => __( 'Sale Description', 'notificationx' ),
1192 'name' => 'exit_intent_sale_desc',
1193 'type' => 'text',
1194 'priority' => 30,
1195 'default' => __( 'ON ENTIRE ORDER', 'notificationx' ),
1196 ],
1197 [
1198 'label' => __( 'Button Text', 'notificationx' ),
1199 'name' => 'exit_intent_button_text',
1200 'type' => 'text',
1201 'priority' => 60,
1202 'default' => __( 'Shop The Flash Sale Now', 'notificationx' ),
1203 ],
1204 [
1205 'label' => __( 'Button URL', 'notificationx' ),
1206 'name' => 'exit_intent_button_url',
1207 'type' => 'text',
1208 'priority' => 61,
1209 'default' => '',
1210 'help' => __( 'Where the CTA button sends visitors. Leave empty to just dismiss the popup.', 'notificationx' ),
1211 ],
1212 [
1213 'label' => __( 'Open in New Tab', 'notificationx' ),
1214 'name' => 'exit_intent_button_new_tab',
1215 'type' => 'toggle',
1216 'priority' => 62,
1217 'default' => true,
1218 ],
1219 [
1220 'label' => __( 'Dismiss Link Text', 'notificationx' ),
1221 'name' => 'exit_intent_dismiss_text',
1222 'type' => 'text',
1223 'priority' => 70,
1224 'default' => __( 'NO, THANKS!', 'notificationx' ),
1225 ],
1226 ],
1227 ];
1228
1229 // ── Theme Three content fields ────────────────────────────────────────────
1230 $fields['exit_intent_theme_three_section'] = [
1231 'label' => __( 'Exit Intent Content', 'notificationx' ),
1232 'name' => 'exit_intent_theme_three_section',
1233 'type' => 'section',
1234 'priority' => 5,
1235 'rules' => Rules::logicalRule( [
1236 Rules::is( 'source', $this->id ),
1237 Rules::is( 'themes', $this->id . '_theme-three' ),
1238 ] ),
1239 'fields' => [
1240 [
1241 'label' => __( 'Character Image', 'notificationx' ),
1242 'name' => 'exit_intent_image_url',
1243 'type' => 'media',
1244 'priority' => 5,
1245 'default' => [
1246 'url' => 'https://notificationx.com/wp-content/uploads/2026/05/exit-intend-theme-three.png',
1247 ],
1248 'help' => __( 'Upload or select a character/illustration image. It will appear above the popup card.', 'notificationx' ),
1249 ],
1250 [
1251 'label' => __( 'Headline', 'notificationx' ),
1252 'name' => 'exit_intent_t3_title',
1253 'type' => 'text',
1254 'priority' => 10,
1255 'default' => __( "Wait, don't go!", 'notificationx' ),
1256 ],
1257 [
1258 'label' => __( 'Subtitle', 'notificationx' ),
1259 'name' => 'exit_intent_t3_subtitle',
1260 'type' => 'text',
1261 'priority' => 20,
1262 'default' => __( 'Before you leave, we have a special offer just for you!', 'notificationx' ),
1263 ],
1264 [
1265 'label' => __( 'Offer Text', 'notificationx' ),
1266 'name' => 'exit_intent_t3_offer',
1267 'type' => 'text',
1268 'priority' => 30,
1269 'default' => __( 'Get 15% off your next purchase!', 'notificationx' ),
1270 ],
1271 [
1272 'label' => __( 'Coupon / Details Text', 'notificationx' ),
1273 'name' => 'exit_intent_t3_coupon_text',
1274 'type' => 'text',
1275 'priority' => 40,
1276 'default' => __( "Use code STAY15 at checkout. Don't miss out on this limited-time offer.", 'notificationx' ),
1277 ],
1278 [
1279 'label' => __( 'Button Text', 'notificationx' ),
1280 'name' => 'exit_intent_button_text',
1281 'type' => 'text',
1282 'priority' => 50,
1283 'default' => __( 'Claim Offer', 'notificationx' ),
1284 ],
1285 [
1286 'label' => __( 'Button URL', 'notificationx' ),
1287 'name' => 'exit_intent_button_url',
1288 'type' => 'text',
1289 'priority' => 51,
1290 'default' => '',
1291 'help' => __( 'Where the CTA button sends visitors. Leave empty to just dismiss the popup.', 'notificationx' ),
1292 ],
1293 [
1294 'label' => __( 'Open in New Tab', 'notificationx' ),
1295 'name' => 'exit_intent_button_new_tab',
1296 'type' => 'toggle',
1297 'priority' => 52,
1298 'default' => true,
1299 ],
1300 [
1301 'label' => __( 'Dismiss Link Text', 'notificationx' ),
1302 'name' => 'exit_intent_dismiss_text',
1303 'type' => 'text',
1304 'priority' => 60,
1305 'default' => __( 'No, thanks!', 'notificationx' ),
1306 ],
1307 ],
1308 ];
1309
1310 // ── Theme Four content fields ─────────────────────────────────────────────
1311 $fields['exit_intent_theme_four_section'] = [
1312 'label' => __( 'Exit Intent Content', 'notificationx' ),
1313 'name' => 'exit_intent_theme_four_section',
1314 'type' => 'section',
1315 'priority' => 5,
1316 'rules' => Rules::logicalRule( [
1317 Rules::is( 'source', $this->id ),
1318 Rules::is( 'themes', $this->id . '_theme-four' ),
1319 ] ),
1320 'fields' => [
1321 [
1322 'label' => __( 'Video Thumbnail', 'notificationx' ),
1323 'name' => 'exit_intent_image_url',
1324 'type' => 'media',
1325 'priority' => 5,
1326 'default' => [
1327 'url' => 'https://notificationx.com/wp-content/uploads/2026/05/exit-intend-theme-four.jpg',
1328 ],
1329 'help' => __( 'Upload or select the video thumbnail/preview image.', 'notificationx' ),
1330 ],
1331 [
1332 'label' => __( 'Badge Text', 'notificationx' ),
1333 'name' => 'exit_intent_t4_badge',
1334 'type' => 'text',
1335 'priority' => 10,
1336 'default' => __( 'Before you go...', 'notificationx' ),
1337 ],
1338 [
1339 'label' => __( 'Headline', 'notificationx' ),
1340 'name' => 'exit_intent_t4_title',
1341 'type' => 'text',
1342 'priority' => 20,
1343 'default' => __( 'Watch this short demo video', 'notificationx' ),
1344 ],
1345 [
1346 'label' => __( 'Subtitle', 'notificationx' ),
1347 'name' => 'exit_intent_t4_subtitle',
1348 'type' => 'text',
1349 'priority' => 30,
1350 'default' => __( 'See how our product simplifies your workflow.', 'notificationx' ),
1351 ],
1352 [
1353 'label' => __( 'Video URL', 'notificationx' ),
1354 'name' => 'exit_intent_t4_video_url',
1355 'type' => 'text',
1356 'priority' => 40,
1357 'default' => '',
1358 'placeholder' => 'https://www.youtube.com/watch?v=...',
1359 'help' => __( 'Paste a YouTube, Vimeo, or other video platform URL.', 'notificationx' ),
1360 ],
1361 ],
1362 ];
1363
1364 // ── Theme Five content fields ────────────────────────────────────────────
1365 $fields['exit_intent_theme_five_section'] = [
1366 'label' => __( 'Exit Intent Content', 'notificationx' ),
1367 'name' => 'exit_intent_theme_five_section',
1368 'type' => 'section',
1369 'priority' => 5,
1370 'rules' => Rules::logicalRule( [
1371 Rules::is( 'source', $this->id ),
1372 Rules::is( 'themes', $this->id . '_theme-five' ),
1373 ] ),
1374 'fields' => [
1375 [
1376 'label' => __( 'Right Panel Image', 'notificationx' ),
1377 'name' => 'exit_intent_image_url',
1378 'type' => 'media',
1379 'priority' => 5,
1380 'default' => [
1381 'url' => 'https://notificationx.com/wp-content/uploads/2026/05/exit-intend-theme-two-five-seven.jpg',
1382 ],
1383 'help' => __( 'Upload or select the image to display in the right panel.', 'notificationx' ),
1384 ],
1385 [
1386 'label' => __( 'Title', 'notificationx' ),
1387 'name' => 'exit_intent_t5_title',
1388 'type' => 'text',
1389 'priority' => 10,
1390 'default' => __( 'Flash Sale', 'notificationx' ),
1391 ],
1392 [
1393 'label' => __( 'Headline', 'notificationx' ),
1394 'name' => 'exit_intent_t5_headline',
1395 'type' => 'text',
1396 'priority' => 20,
1397 'default' => __( '50% OFF', 'notificationx' ),
1398 ],
1399 [
1400 'label' => __( 'Description', 'notificationx' ),
1401 'name' => 'exit_intent_t5_desc',
1402 'type' => 'text',
1403 'priority' => 30,
1404 'default' => __( 'ON ENTIRE ORDER', 'notificationx' ),
1405 ],
1406 [
1407 'label' => __( 'Button Text', 'notificationx' ),
1408 'name' => 'exit_intent_button_text',
1409 'type' => 'text',
1410 'priority' => 60,
1411 'default' => __( 'Shop The Flash Sale Now', 'notificationx' ),
1412 ],
1413 [
1414 'label' => __( 'Button URL', 'notificationx' ),
1415 'name' => 'exit_intent_button_url',
1416 'type' => 'text',
1417 'priority' => 61,
1418 'default' => '',
1419 'help' => __( 'Where the CTA button sends visitors. Leave empty to just dismiss the popup.', 'notificationx' ),
1420 ],
1421 [
1422 'label' => __( 'Open in New Tab', 'notificationx' ),
1423 'name' => 'exit_intent_button_new_tab',
1424 'type' => 'toggle',
1425 'priority' => 62,
1426 'default' => true,
1427 ],
1428 [
1429 'label' => __( 'Dismiss Link Text', 'notificationx' ),
1430 'name' => 'exit_intent_dismiss_text',
1431 'type' => 'text',
1432 'priority' => 70,
1433 'default' => __( 'NO, THANKS!', 'notificationx' ),
1434 ],
1435 ],
1436 ];
1437
1438 // ── Theme Five Timer Settings ────────────────────────────────────────────
1439 $fields['exit_intent_theme_five_timer_section'] = [
1440 'label' => __( 'Timer Settings', 'notificationx' ),
1441 'name' => 'exit_intent_theme_five_timer_section',
1442 'type' => 'section',
1443 'priority' => 6,
1444 'rules' => Rules::logicalRule( [
1445 Rules::is( 'source', $this->id ),
1446 Rules::is( 'themes', $this->id . '_theme-five' ),
1447 ] ),
1448 'fields' => [
1449 [
1450 'label' => __( 'Show Countdown Timer', 'notificationx' ),
1451 'name' => 'exit_intent_t5_show_timer',
1452 'type' => 'toggle',
1453 'priority' => 5,
1454 'default' => true,
1455 ],
1456 [
1457 'label' => __( 'Countdown Label', 'notificationx' ),
1458 'name' => 'exit_intent_t5_countdown_label',
1459 'type' => 'text',
1460 'priority' => 10,
1461 'default' => __( 'LIMITED-TIME OFFER! SALE ENDS IN', 'notificationx' ),
1462 'rules' => Rules::is( 'exit_intent_t5_show_timer', true ),
1463 ],
1464 [
1465 'label' => __( 'Sale End Date & Time', 'notificationx' ),
1466 'name' => 'exit_intent_countdown_end',
1467 'type' => 'date',
1468 'priority' => 20,
1469 'default' => '',
1470 'help' => __( 'Pick the date and time when the sale ends. Leave empty to display static demo numbers.', 'notificationx' ),
1471 'rules' => Rules::is( 'exit_intent_t5_show_timer', true ),
1472 ],
1473 [
1474 'label' => __( 'Days Label', 'notificationx' ),
1475 'name' => 'exit_intent_t5_days_label',
1476 'type' => 'text',
1477 'priority' => 30,
1478 'default' => __( 'DAYS', 'notificationx' ),
1479 'rules' => Rules::is( 'exit_intent_t5_show_timer', true ),
1480 ],
1481 [
1482 'label' => __( 'Hours Label', 'notificationx' ),
1483 'name' => 'exit_intent_t5_hours_label',
1484 'type' => 'text',
1485 'priority' => 40,
1486 'default' => __( 'HRS', 'notificationx' ),
1487 'rules' => Rules::is( 'exit_intent_t5_show_timer', true ),
1488 ],
1489 [
1490 'label' => __( 'Minutes Label', 'notificationx' ),
1491 'name' => 'exit_intent_t5_minutes_label',
1492 'type' => 'text',
1493 'priority' => 50,
1494 'default' => __( 'MIN', 'notificationx' ),
1495 'rules' => Rules::is( 'exit_intent_t5_show_timer', true ),
1496 ],
1497 [
1498 'label' => __( 'Seconds Label', 'notificationx' ),
1499 'name' => 'exit_intent_t5_seconds_label',
1500 'type' => 'text',
1501 'priority' => 60,
1502 'default' => __( 'SEC', 'notificationx' ),
1503 'rules' => Rules::is( 'exit_intent_t5_show_timer', true ),
1504 ],
1505 [
1506 'label' => __( 'Timer Box Background', 'notificationx' ),
1507 'name' => 'exit_intent_t5_timer_bg',
1508 'type' => 'colorpicker',
1509 'priority' => 70,
1510 'default' => '#fff0f5',
1511 'rules' => Rules::is( 'exit_intent_t5_show_timer', true ),
1512 ],
1513 [
1514 'label' => __( 'Timer Number Color', 'notificationx' ),
1515 'name' => 'exit_intent_t5_timer_color',
1516 'type' => 'colorpicker',
1517 'priority' => 80,
1518 'default' => '#e91e63',
1519 'rules' => Rules::is( 'exit_intent_t5_show_timer', true ),
1520 ],
1521 ],
1522 ];
1523
1524 // ── Theme Seven content fields ───────────────────────────────────────────
1525 $fields['exit_intent_theme_seven_section'] = [
1526 'label' => __( 'Exit Intent Content', 'notificationx' ),
1527 'name' => 'exit_intent_theme_seven_section',
1528 'type' => 'section',
1529 'priority' => 5,
1530 'rules' => Rules::logicalRule( [
1531 Rules::is( 'source', $this->id ),
1532 Rules::is( 'themes', $this->id . '_theme-seven' ),
1533 ] ),
1534 'fields' => [
1535 [
1536 'label' => __( 'Left Panel Image', 'notificationx' ),
1537 'name' => 'exit_intent_image_url',
1538 'type' => 'media',
1539 'priority' => 5,
1540 'default' => [
1541 'url' => 'https://notificationx.com/wp-content/uploads/2026/05/exit-intend-theme-two-five-seven.jpg',
1542 ],
1543 'help' => __( 'Upload or select the image to display in the left panel.', 'notificationx' ),
1544 ],
1545 [
1546 'label' => __( 'Headline', 'notificationx' ),
1547 'name' => 'exit_intent_t7_headline',
1548 'type' => 'text',
1549 'priority' => 10,
1550 'default' => __( 'Home Is Where Your Story Begins', 'notificationx' ),
1551 ],
1552 [
1553 'label' => __( 'Discount Banner Text', 'notificationx' ),
1554 'name' => 'exit_intent_t7_discount_text',
1555 'type' => 'text',
1556 'priority' => 20,
1557 'default' => __( 'Get 15% Off Your First Order!', 'notificationx' ),
1558 ],
1559 [
1560 'label' => __( 'Description', 'notificationx' ),
1561 'name' => 'exit_intent_t7_description',
1562 'type' => 'text',
1563 'priority' => 30,
1564 'default' => __( 'Discover timeless pieces that turn any space into a sanctuary.', 'notificationx' ),
1565 ],
1566 [
1567 'label' => __( 'Email Placeholder', 'notificationx' ),
1568 'name' => 'exit_intent_t7_email_placeholder',
1569 'type' => 'text',
1570 'priority' => 40,
1571 'default' => __( 'Enter your email', 'notificationx' ),
1572 ],
1573 [
1574 'label' => __( 'Button Text', 'notificationx' ),
1575 'name' => 'exit_intent_button_text',
1576 'type' => 'text',
1577 'priority' => 50,
1578 'default' => __( 'SEND COUPON', 'notificationx' ),
1579 ],
1580 ],
1581 ];
1582
1583 // ── Theme Six content fields ─────────────────────────────────────────────
1584 $fields['exit_intent_theme_six_section'] = [
1585 'label' => __( 'Exit Intent Content', 'notificationx' ),
1586 'name' => 'exit_intent_theme_six_section',
1587 'type' => 'section',
1588 'priority' => 5,
1589 'rules' => Rules::logicalRule( [
1590 Rules::is( 'source', $this->id ),
1591 Rules::is( 'themes', $this->id . '_theme-six' ),
1592 ] ),
1593 'fields' => [
1594 [
1595 'label' => __( 'Product Image', 'notificationx' ),
1596 'name' => 'exit_intent_image_url',
1597 'type' => 'media',
1598 'priority' => 5,
1599 'default' => [
1600 'url' => 'https://notificationx.com/wp-content/uploads/2026/05/exit-intend-theme-six.png',
1601 ],
1602 'help' => __( 'Upload or select the product image to display at the top of the popup.', 'notificationx' ),
1603 ],
1604 [
1605 'label' => __( 'Headline', 'notificationx' ),
1606 'name' => 'exit_intent_t6_title',
1607 'type' => 'text',
1608 'priority' => 10,
1609 'default' => __( 'Limited Edition Bass Boost Headphones', 'notificationx' ),
1610 ],
1611 [
1612 'label' => __( 'Button Text', 'notificationx' ),
1613 'name' => 'exit_intent_button_text',
1614 'type' => 'text',
1615 'priority' => 60,
1616 'default' => __( 'Grab Now', 'notificationx' ),
1617 ],
1618 [
1619 'label' => __( 'Button URL', 'notificationx' ),
1620 'name' => 'exit_intent_button_url',
1621 'type' => 'text',
1622 'priority' => 61,
1623 'default' => '',
1624 'help' => __( 'Where the CTA button sends visitors. Leave empty to just dismiss the popup.', 'notificationx' ),
1625 ],
1626 [
1627 'label' => __( 'Open in New Tab', 'notificationx' ),
1628 'name' => 'exit_intent_button_new_tab',
1629 'type' => 'toggle',
1630 'priority' => 62,
1631 'default' => true,
1632 ],
1633 ],
1634 ];
1635
1636 // ── Theme Six Timer Settings ─────────────────────────────────────────────
1637 $fields['exit_intent_theme_six_timer_section'] = [
1638 'label' => __( 'Timer Settings', 'notificationx' ),
1639 'name' => 'exit_intent_theme_six_timer_section',
1640 'type' => 'section',
1641 'priority' => 6,
1642 'rules' => Rules::logicalRule( [
1643 Rules::is( 'source', $this->id ),
1644 Rules::is( 'themes', $this->id . '_theme-six' ),
1645 ] ),
1646 'fields' => [
1647 [
1648 'label' => __( 'Show Countdown Timer', 'notificationx' ),
1649 'name' => 'exit_intent_t6_show_timer',
1650 'type' => 'toggle',
1651 'priority' => 5,
1652 'default' => true,
1653 ],
1654 [
1655 'label' => __( 'Countdown Label', 'notificationx' ),
1656 'name' => 'exit_intent_t6_countdown_label',
1657 'type' => 'text',
1658 'priority' => 10,
1659 'default' => __( 'Offer Ends In', 'notificationx' ),
1660 'rules' => Rules::is( 'exit_intent_t6_show_timer', true ),
1661 ],
1662 [
1663 'label' => __( 'Sale End Date & Time', 'notificationx' ),
1664 'name' => 'exit_intent_countdown_end',
1665 'type' => 'date',
1666 'priority' => 20,
1667 'default' => '',
1668 'help' => __( 'Pick the date and time when the sale ends. Leave empty to display static demo numbers.', 'notificationx' ),
1669 'rules' => Rules::is( 'exit_intent_t6_show_timer', true ),
1670 ],
1671 [
1672 'label' => __( 'Days Label', 'notificationx' ),
1673 'name' => 'exit_intent_t6_days_label',
1674 'type' => 'text',
1675 'priority' => 30,
1676 'default' => __( 'DAYS', 'notificationx' ),
1677 'rules' => Rules::is( 'exit_intent_t6_show_timer', true ),
1678 ],
1679 [
1680 'label' => __( 'Hours Label', 'notificationx' ),
1681 'name' => 'exit_intent_t6_hours_label',
1682 'type' => 'text',
1683 'priority' => 40,
1684 'default' => __( 'HOURS', 'notificationx' ),
1685 'rules' => Rules::is( 'exit_intent_t6_show_timer', true ),
1686 ],
1687 [
1688 'label' => __( 'Minutes Label', 'notificationx' ),
1689 'name' => 'exit_intent_t6_minutes_label',
1690 'type' => 'text',
1691 'priority' => 50,
1692 'default' => __( 'MIN', 'notificationx' ),
1693 'rules' => Rules::is( 'exit_intent_t6_show_timer', true ),
1694 ],
1695 [
1696 'label' => __( 'Seconds Label', 'notificationx' ),
1697 'name' => 'exit_intent_t6_seconds_label',
1698 'type' => 'text',
1699 'priority' => 60,
1700 'default' => __( 'SEC', 'notificationx' ),
1701 'rules' => Rules::is( 'exit_intent_t6_show_timer', true ),
1702 ],
1703 ],
1704 ];
1705
1706 // ── Theme One content fields ─────────────────────────────────────────────
1707 $fields['exit_intent_content_section'] = [
1708 'label' => __( 'Exit Intent Content', 'notificationx' ),
1709 'name' => 'exit_intent_content_section',
1710 'type' => 'section',
1711 'priority' => 5,
1712 'rules' => Rules::logicalRule( [
1713 Rules::is( 'source', $this->id ),
1714 Rules::is( 'themes', $this->id . '_theme-two', true ),
1715 Rules::is( 'themes', $this->id . '_theme-three', true ),
1716 Rules::is( 'themes', $this->id . '_theme-four', true ),
1717 Rules::is( 'themes', $this->id . '_theme-five', true ),
1718 Rules::is( 'themes', $this->id . '_theme-six', true ),
1719 Rules::is( 'themes', $this->id . '_theme-seven', true ),
1720 ] ),
1721 'fields' => [
1722 // ── Main copy ────────────────────────────────────────────
1723 [
1724 'label' => __( 'Title', 'notificationx' ),
1725 'name' => 'exit_intent_title',
1726 'type' => 'text',
1727 'priority' => 10,
1728 'default' => __( 'Wait! Before You Go...', 'notificationx' ),
1729 ],
1730 [
1731 'label' => __( 'Subtitle', 'notificationx' ),
1732 'name' => 'exit_intent_subtitle',
1733 'type' => 'text',
1734 'priority' => 20,
1735 'default' => __( "We'd love to understand what's holding you back", 'notificationx' ),
1736 ],
1737 // ── Form fields ──────────────────────────────────────────
1738 [
1739 'label' => __( 'Show Name Field', 'notificationx' ),
1740 'name' => 'exit_intent_show_name',
1741 'type' => 'toggle',
1742 'default' => true,
1743 'priority' => 70,
1744 'is_pro' => true,
1745 ],
1746 [
1747 'label' => __( 'Name Placeholder', 'notificationx' ),
1748 'name' => 'exit_intent_name_label',
1749 'type' => 'text',
1750 'default' => __( 'Name *', 'notificationx' ),
1751 'priority' => 75,
1752 'is_pro' => true,
1753 'rules' => Rules::is( 'exit_intent_show_name', true ),
1754 ],
1755 [
1756 'label' => __( 'Show Email Field', 'notificationx' ),
1757 'name' => 'exit_intent_show_email',
1758 'type' => 'toggle',
1759 'default' => true,
1760 'priority' => 80,
1761 'is_pro' => true,
1762 ],
1763 [
1764 'label' => __( 'Email Placeholder', 'notificationx' ),
1765 'name' => 'exit_intent_email_label',
1766 'type' => 'text',
1767 'default' => __( 'Enter Your Email *', 'notificationx' ),
1768 'priority' => 85,
1769 'is_pro' => true,
1770 'rules' => Rules::is( 'exit_intent_show_email', true ),
1771 ],
1772 [
1773 'label' => __( 'Show Message Field', 'notificationx' ),
1774 'name' => 'exit_intent_show_message',
1775 'type' => 'toggle',
1776 'default' => true,
1777 'priority' => 87,
1778 ],
1779 [
1780 'label' => __( 'Message Placeholder', 'notificationx' ),
1781 'name' => 'exit_intent_message_placeholder',
1782 'type' => 'text',
1783 'default' => __( 'Your message...', 'notificationx' ),
1784 'priority' => 88,
1785 'rules' => Rules::is( 'exit_intent_show_message', true ),
1786 ],
1787
1788 // ── Button ───────────────────────────────────────────────
1789 [
1790 'label' => __( 'Button Text', 'notificationx' ),
1791 'name' => 'exit_intent_button_text',
1792 'type' => 'text',
1793 'default' => __( 'SUBMIT', 'notificationx' ),
1794 'priority' => 90,
1795 ],
1796 ],
1797 ];
1798
1799 return $fields;
1800 }
1801
1802 public function design_fields( $fields ) {
1803 // Hide the inner sub-sections of the global advance_design_section for this source,
1804 // but keep advance_design_section itself visible so its `advance_edit` toggle renders.
1805 foreach ( [ 'design', 'typography', 'image-appearance', 'link_button_design' ] as $key ) {
1806 if ( isset( $fields['advance_design_section']['fields'][ $key ] ) ) {
1807 $fields['advance_design_section']['fields'][ $key ] = Rules::is(
1808 'source', $this->id, true, $fields['advance_design_section']['fields'][ $key ]
1809 );
1810 }
1811 }
1812
1813 // Inline every theme's design fields directly into advance_design_section so they appear
1814 // flat under the existing "Advanced Design" heading — no per-theme sub-headers.
1815 // Priorities are auto-assigned starting at 20 so all theme controls sort BEFORE the
1816 // global Custom CSS section (priority 150).
1817 $design = &$fields['advance_design_section']['fields'];
1818 $priority = 20;
1819 $merge = function( $theme_slug, $list ) use ( &$design, &$priority ) {
1820 $rule = $this->theme_design_rules( $theme_slug );
1821 foreach ( $list as $field ) {
1822 if ( ! empty( $field['rules'] ) ) {
1823 $field['rules'] = Rules::logicalRule( [ $rule, $field['rules'] ] );
1824 } else {
1825 $field['rules'] = $rule;
1826 }
1827 if ( ! isset( $field['priority'] ) ) {
1828 $field['priority'] = $priority;
1829 }
1830 $priority++;
1831 $design[ $field['name'] ] = $field;
1832 }
1833 };
1834
1835 $merge( 'theme-one', $this->theme_one_design_fields() );
1836 $merge( 'theme-two', $this->theme_two_design_fields() );
1837 $merge( 'theme-three', $this->theme_three_design_fields() );
1838 $merge( 'theme-four', $this->theme_four_design_fields() );
1839 $merge( 'theme-five', $this->theme_five_design_fields() );
1840 $merge( 'theme-six', $this->theme_six_design_fields() );
1841 $merge( 'theme-seven', $this->theme_seven_design_fields() );
1842
1843 return $fields;
1844 }
1845
1846 /** Build a rule scoped to source + advance_edit + a specific theme. */
1847 private function theme_design_rules( $theme_slug ) {
1848 return Rules::logicalRule( [
1849 Rules::is( 'source', $this->id ),
1850 Rules::is( 'advance_edit', true ),
1851 Rules::is( 'themes', $this->id . '_' . $theme_slug ),
1852 ] );
1853 }
1854
1855 private function font_weight_options() {
1856 return GlobalFields::get_instance()->normalize_fields( [
1857 '400' => __( 'Normal (400)', 'notificationx' ),
1858 '500' => __( 'Medium (500)', 'notificationx' ),
1859 '600' => __( 'Semi Bold (600)', 'notificationx' ),
1860 '700' => __( 'Bold (700)', 'notificationx' ),
1861 '800' => __( 'Extra Bold (800)', 'notificationx' ),
1862 ] );
1863 }
1864
1865 /** ───────────────────────── Theme One — Feedback Form ───────────────────────── */
1866 private function theme_one_design_fields() {
1867 return [
1868 // Container
1869 [ 'label' => __( 'Popup Max Width', 'notificationx' ), 'name' => 'exit_intent_max_width', 'type' => 'number', 'default' => 540, 'description' => 'px' ],
1870 [ 'label' => __( 'Border Radius', 'notificationx' ), 'name' => 'exit_intent_border_radius', 'type' => 'number', 'default' => 12, 'description' => 'px' ],
1871 [ 'label' => __( 'Background Color', 'notificationx' ), 'name' => 'exit_intent_bg_color', 'type' => 'colorpicker', 'default' => '#EDE7FF' ],
1872 [ 'label' => __( 'Overlay Background Color', 'notificationx' ),'name' => 'exit_intent_overlay_color', 'type' => 'colorpicker', 'default' => 'rgba(0,0,0,0.5)',
1873 'help' => __( 'Color of the backdrop behind the popup.', 'notificationx' ) ],
1874 [ 'label' => __( 'Show Background Pattern', 'notificationx' ), 'name' => 'exit_intent_show_pattern', 'type' => 'toggle', 'default' => true ],
1875 [ 'label' => __( 'Pattern Color', 'notificationx' ), 'name' => 'exit_intent_pattern_color', 'type' => 'colorpicker', 'default' => '#D4C3FF',
1876 'rules' => Rules::is( 'exit_intent_show_pattern', true ) ],
1877
1878 // Title
1879 [ 'label' => __( 'Title Color', 'notificationx' ), 'name' => 'exit_intent_title_color', 'type' => 'colorpicker', 'default' => '#1a1a2e' ],
1880 [ 'label' => __( 'Title Font Size', 'notificationx' ), 'name' => 'exit_intent_title_font_size', 'type' => 'number', 'default' => 26, 'description' => 'px' ],
1881 [ 'label' => __( 'Title Font Weight', 'notificationx' ), 'name' => 'exit_intent_title_font_weight', 'type' => 'select', 'default' => '700',
1882 'options' => $this->font_weight_options() ],
1883
1884 // Subtitle
1885 [ 'label' => __( 'Subtitle Color', 'notificationx' ), 'name' => 'exit_intent_subtitle_color', 'type' => 'colorpicker', 'default' => '#4a4a6a' ],
1886 [ 'label' => __( 'Subtitle Font Size', 'notificationx' ), 'name' => 'exit_intent_subtitle_font_size', 'type' => 'number', 'default' => 14, 'description' => 'px' ],
1887
1888 // Inputs
1889 [ 'label' => __( 'Input Background Color', 'notificationx' ), 'name' => 'exit_intent_input_bg', 'type' => 'colorpicker', 'default' => '#ffffff' ],
1890 [ 'label' => __( 'Input Border Color', 'notificationx' ), 'name' => 'exit_intent_input_border_color', 'type' => 'colorpicker', 'default' => '#dddddd' ],
1891 [ 'label' => __( 'Input Border Radius', 'notificationx' ), 'name' => 'exit_intent_input_border_radius', 'type' => 'number', 'default' => 6, 'description' => 'px' ],
1892 [ 'label' => __( 'Input Text Color', 'notificationx' ), 'name' => 'exit_intent_input_text_color', 'type' => 'colorpicker', 'default' => '#333333' ],
1893
1894 // Button
1895 [ 'label' => __( 'Button Background Color', 'notificationx' ), 'name' => 'exit_intent_btn_bg', 'type' => 'colorpicker', 'default' => '#6B21A8' ],
1896 [ 'label' => __( 'Button Text Color', 'notificationx' ), 'name' => 'exit_intent_btn_color', 'type' => 'colorpicker', 'default' => '#ffffff' ],
1897 [ 'label' => __( 'Button Border Radius', 'notificationx' ), 'name' => 'exit_intent_btn_border_radius', 'type' => 'number', 'default' => 6, 'description' => 'px' ],
1898 [ 'label' => __( 'Button Font Size', 'notificationx' ), 'name' => 'exit_intent_btn_font_size', 'type' => 'number', 'default' => 14, 'description' => 'px' ],
1899 [ 'label' => __( 'Button Font Weight', 'notificationx' ), 'name' => 'exit_intent_btn_font_weight', 'type' => 'select', 'default' => '700',
1900 'options' => $this->font_weight_options() ],
1901
1902 // Close button
1903 [ 'label' => __( 'Close Button Color', 'notificationx' ), 'name' => 'exit_intent_close_color', 'type' => 'colorpicker', 'default' => '#666666' ],
1904 [ 'label' => __( 'Close Button Size', 'notificationx' ), 'name' => 'exit_intent_close_size', 'type' => 'number', 'default' => 20, 'description' => 'px' ],
1905 ];
1906 }
1907
1908 /** ───────────────────────── Theme Two — Flash Sale w/ Countdown ───────────────────────── */
1909 private function theme_two_design_fields() {
1910 return [
1911 // Container / overlay / close
1912 [ 'label' => __( 'Popup Max Width', 'notificationx' ), 'name' => 'exit_intent_t2_max_width', 'type' => 'number', 'default' => 760, 'description' => 'px' ],
1913 [ 'label' => __( 'Border Radius', 'notificationx' ), 'name' => 'exit_intent_t2_border_radius', 'type' => 'number', 'default' => 12, 'description' => 'px' ],
1914 [ 'label' => __( 'Left Panel Background', 'notificationx' ), 'name' => 'exit_intent_t2_bg_color', 'type' => 'colorpicker', 'default' => '#ffffff' ],
1915 [ 'label' => __( 'Overlay Background Color', 'notificationx' ), 'name' => 'exit_intent_overlay_color', 'type' => 'colorpicker', 'default' => 'rgba(0,0,0,0.5)' ],
1916 [ 'label' => __( 'Close Button Color', 'notificationx' ), 'name' => 'exit_intent_close_color', 'type' => 'colorpicker', 'default' => '#666666' ],
1917 [ 'label' => __( 'Close Button Size', 'notificationx' ), 'name' => 'exit_intent_close_size', 'type' => 'number', 'default' => 20, 'description' => 'px' ],
1918
1919 // Sale badge
1920 [ 'label' => __( 'Badge Background', 'notificationx' ), 'name' => 'exit_intent_t2_badge_bg', 'type' => 'colorpicker', 'default' => '#ffe4ec' ],
1921 [ 'label' => __( 'Badge Text Color', 'notificationx' ),'name' => 'exit_intent_t2_badge_color', 'type' => 'colorpicker', 'default' => '#e91e63' ],
1922 [ 'label' => __( 'Badge Font Size', 'notificationx' ), 'name' => 'exit_intent_t2_badge_font_size', 'type' => 'number', 'default' => 12, 'description' => 'px' ],
1923
1924 // Headline
1925 [ 'label' => __( 'Headline Color', 'notificationx' ), 'name' => 'exit_intent_t2_headline_color', 'type' => 'colorpicker', 'default' => '#1a1a2e' ],
1926 [ 'label' => __( 'Headline Font Size', 'notificationx' ), 'name' => 'exit_intent_t2_headline_font_size', 'type' => 'number', 'default' => 56, 'description' => 'px' ],
1927 [ 'label' => __( 'Headline Font Weight', 'notificationx' ), 'name' => 'exit_intent_t2_headline_font_weight', 'type' => 'select', 'default' => '800',
1928 'options' => $this->font_weight_options() ],
1929
1930 // Description
1931 [ 'label' => __( 'Description Color', 'notificationx' ), 'name' => 'exit_intent_t2_desc_color', 'type' => 'colorpicker', 'default' => '#4a4a6a' ],
1932 [ 'label' => __( 'Description Font Size', 'notificationx' ), 'name' => 'exit_intent_t2_desc_font_size', 'type' => 'number', 'default' => 14, 'description' => 'px' ],
1933
1934 // CTA button
1935 [ 'label' => __( 'Button Background', 'notificationx' ), 'name' => 'exit_intent_t2_btn_bg', 'type' => 'colorpicker', 'default' => '#e91e63' ],
1936 [ 'label' => __( 'Button Text Color', 'notificationx' ), 'name' => 'exit_intent_t2_btn_color', 'type' => 'colorpicker', 'default' => '#ffffff' ],
1937 [ 'label' => __( 'Button Border Radius', 'notificationx' ), 'name' => 'exit_intent_t2_btn_border_radius', 'type' => 'number', 'default' => 6, 'description' => 'px' ],
1938 [ 'label' => __( 'Button Font Size', 'notificationx' ), 'name' => 'exit_intent_t2_btn_font_size', 'type' => 'number', 'default' => 14, 'description' => 'px' ],
1939 [ 'label' => __( 'Button Font Weight', 'notificationx' ), 'name' => 'exit_intent_t2_btn_font_weight', 'type' => 'select', 'default' => '700',
1940 'options' => $this->font_weight_options() ],
1941
1942 // Dismiss link
1943 [ 'label' => __( 'Dismiss Text Color', 'notificationx' ), 'name' => 'exit_intent_t2_dismiss_color', 'type' => 'colorpicker', 'default' => '#9a9aa8' ],
1944 [ 'label' => __( 'Dismiss Font Size', 'notificationx' ), 'name' => 'exit_intent_t2_dismiss_font_size', 'type' => 'number', 'default' => 12, 'description' => 'px' ],
1945 ];
1946 }
1947
1948 /** ───────────────────────── Theme Three — Coupon Offer ───────────────────────── */
1949 private function theme_three_design_fields() {
1950 return [
1951 // Container / overlay / close
1952 [ 'label' => __( 'Popup Max Width', 'notificationx' ), 'name' => 'exit_intent_t3_max_width', 'type' => 'number', 'default' => 460, 'description' => 'px' ],
1953 [ 'label' => __( 'Border Radius', 'notificationx' ), 'name' => 'exit_intent_t3_border_radius', 'type' => 'number', 'default' => 16, 'description' => 'px' ],
1954 [ 'label' => __( 'Background Color', 'notificationx' ), 'name' => 'exit_intent_t3_bg_color', 'type' => 'colorpicker', 'default' => '#ffffff' ],
1955 [ 'label' => __( 'Overlay Background Color', 'notificationx' ), 'name' => 'exit_intent_overlay_color', 'type' => 'colorpicker', 'default' => 'rgba(0,0,0,0.5)' ],
1956 [ 'label' => __( 'Close Button Color', 'notificationx' ), 'name' => 'exit_intent_close_color', 'type' => 'colorpicker', 'default' => '#666666' ],
1957 [ 'label' => __( 'Close Button Size', 'notificationx' ), 'name' => 'exit_intent_close_size', 'type' => 'number', 'default' => 20, 'description' => 'px' ],
1958
1959 // Title
1960 [ 'label' => __( 'Title Color', 'notificationx' ), 'name' => 'exit_intent_t3_title_color', 'type' => 'colorpicker', 'default' => '#1a1a2e' ],
1961 [ 'label' => __( 'Title Font Size', 'notificationx' ), 'name' => 'exit_intent_t3_title_font_size', 'type' => 'number', 'default' => 26, 'description' => 'px' ],
1962 [ 'label' => __( 'Title Font Weight', 'notificationx' ), 'name' => 'exit_intent_t3_title_font_weight', 'type' => 'select', 'default' => '700',
1963 'options' => $this->font_weight_options() ],
1964
1965 // Subtitle
1966 [ 'label' => __( 'Subtitle Color', 'notificationx' ), 'name' => 'exit_intent_t3_subtitle_color', 'type' => 'colorpicker', 'default' => '#4a4a6a' ],
1967 [ 'label' => __( 'Subtitle Font Size', 'notificationx' ), 'name' => 'exit_intent_t3_subtitle_font_size', 'type' => 'number', 'default' => 14, 'description' => 'px' ],
1968
1969 // Offer line
1970 [ 'label' => __( 'Offer Color', 'notificationx' ), 'name' => 'exit_intent_t3_offer_color', 'type' => 'colorpicker', 'default' => '#e91e63' ],
1971 [ 'label' => __( 'Offer Font Size', 'notificationx' ), 'name' => 'exit_intent_t3_offer_font_size', 'type' => 'number', 'default' => 18, 'description' => 'px' ],
1972 [ 'label' => __( 'Offer Font Weight', 'notificationx' ), 'name' => 'exit_intent_t3_offer_font_weight', 'type' => 'select', 'default' => '700',
1973 'options' => $this->font_weight_options() ],
1974
1975 // Coupon block
1976 [ 'label' => __( 'Coupon Block Background', 'notificationx' ), 'name' => 'exit_intent_t3_coupon_bg', 'type' => 'colorpicker', 'default' => '#fff7fb' ],
1977 [ 'label' => __( 'Coupon Text Color', 'notificationx' ), 'name' => 'exit_intent_t3_coupon_color', 'type' => 'colorpicker', 'default' => '#1a1a2e' ],
1978 [ 'label' => __( 'Coupon Font Size', 'notificationx' ), 'name' => 'exit_intent_t3_coupon_font_size', 'type' => 'number', 'default' => 14, 'description' => 'px' ],
1979 [ 'label' => __( 'Coupon Block Border Radius', 'notificationx' ), 'name' => 'exit_intent_t3_coupon_border_radius', 'type' => 'number', 'default' => 8, 'description' => 'px' ],
1980
1981 // CTA button
1982 [ 'label' => __( 'Button Background', 'notificationx' ), 'name' => 'exit_intent_t3_btn_bg', 'type' => 'colorpicker', 'default' => '#e91e63' ],
1983 [ 'label' => __( 'Button Text Color', 'notificationx' ), 'name' => 'exit_intent_t3_btn_color', 'type' => 'colorpicker', 'default' => '#ffffff' ],
1984 [ 'label' => __( 'Button Border Radius', 'notificationx' ), 'name' => 'exit_intent_t3_btn_border_radius', 'type' => 'number', 'default' => 6, 'description' => 'px' ],
1985 [ 'label' => __( 'Button Font Size', 'notificationx' ), 'name' => 'exit_intent_t3_btn_font_size', 'type' => 'number', 'default' => 14, 'description' => 'px' ],
1986 [ 'label' => __( 'Button Font Weight', 'notificationx' ), 'name' => 'exit_intent_t3_btn_font_weight', 'type' => 'select', 'default' => '700',
1987 'options' => $this->font_weight_options() ],
1988
1989 // Dismiss link
1990 [ 'label' => __( 'Dismiss Text Color', 'notificationx' ), 'name' => 'exit_intent_t3_dismiss_color', 'type' => 'colorpicker', 'default' => '#9a9aa8' ],
1991 [ 'label' => __( 'Dismiss Font Size', 'notificationx' ), 'name' => 'exit_intent_t3_dismiss_font_size', 'type' => 'number', 'default' => 12, 'description' => 'px' ],
1992 ];
1993 }
1994
1995 /** ───────────────────────── Theme Four — Video Popup ───────────────────────── */
1996 private function theme_four_design_fields() {
1997 return [
1998 // Container / overlay / close
1999 [ 'label' => __( 'Popup Max Width', 'notificationx' ), 'name' => 'exit_intent_t4_max_width', 'type' => 'number', 'default' => 520, 'description' => 'px' ],
2000 [ 'label' => __( 'Border Radius', 'notificationx' ), 'name' => 'exit_intent_t4_border_radius', 'type' => 'number', 'default' => 16, 'description' => 'px' ],
2001 [ 'label' => __( 'Background Color', 'notificationx' ), 'name' => 'exit_intent_t4_bg_color', 'type' => 'colorpicker', 'default' => '#f4ecff' ],
2002 [ 'label' => __( 'Overlay Background Color', 'notificationx' ), 'name' => 'exit_intent_overlay_color', 'type' => 'colorpicker', 'default' => 'rgba(0,0,0,0.5)' ],
2003 [ 'label' => __( 'Close Button Color', 'notificationx' ), 'name' => 'exit_intent_close_color', 'type' => 'colorpicker', 'default' => '#666666' ],
2004 [ 'label' => __( 'Close Button Size', 'notificationx' ), 'name' => 'exit_intent_close_size', 'type' => 'number', 'default' => 20, 'description' => 'px' ],
2005
2006 // Badge
2007 [ 'label' => __( 'Badge Background', 'notificationx' ), 'name' => 'exit_intent_t4_badge_bg', 'type' => 'colorpicker', 'default' => '#ffffff' ],
2008 [ 'label' => __( 'Badge Text Color', 'notificationx' ),'name' => 'exit_intent_t4_badge_color', 'type' => 'colorpicker', 'default' => '#6B21A8' ],
2009 [ 'label' => __( 'Badge Font Size', 'notificationx' ), 'name' => 'exit_intent_t4_badge_font_size', 'type' => 'number', 'default' => 12, 'description' => 'px' ],
2010
2011 // Title
2012 [ 'label' => __( 'Title Color', 'notificationx' ), 'name' => 'exit_intent_t4_title_color', 'type' => 'colorpicker', 'default' => '#1a1a2e' ],
2013 [ 'label' => __( 'Title Font Size', 'notificationx' ), 'name' => 'exit_intent_t4_title_font_size', 'type' => 'number', 'default' => 24, 'description' => 'px' ],
2014 [ 'label' => __( 'Title Font Weight', 'notificationx' ), 'name' => 'exit_intent_t4_title_font_weight', 'type' => 'select', 'default' => '700',
2015 'options' => $this->font_weight_options() ],
2016
2017 // Subtitle
2018 [ 'label' => __( 'Subtitle Color', 'notificationx' ), 'name' => 'exit_intent_t4_subtitle_color', 'type' => 'colorpicker', 'default' => '#4a4a6a' ],
2019 [ 'label' => __( 'Subtitle Font Size', 'notificationx' ), 'name' => 'exit_intent_t4_subtitle_font_size', 'type' => 'number', 'default' => 14, 'description' => 'px' ],
2020
2021 // Video wrap + play icon
2022 [ 'label' => __( 'Video Wrapper Background', 'notificationx' ), 'name' => 'exit_intent_t4_video_bg', 'type' => 'colorpicker', 'default' => '#000000' ],
2023 [ 'label' => __( 'Video Wrapper Border Radius', 'notificationx' ), 'name' => 'exit_intent_t4_video_radius', 'type' => 'number', 'default' => 12, 'description' => 'px' ],
2024 [ 'label' => __( 'Play Icon Background', 'notificationx' ), 'name' => 'exit_intent_t4_play_bg', 'type' => 'colorpicker', 'default' => '#ffffff' ],
2025 [ 'label' => __( 'Play Icon Color', 'notificationx' ), 'name' => 'exit_intent_t4_play_color', 'type' => 'colorpicker', 'default' => '#1a1a2e' ],
2026 ];
2027 }
2028
2029 /** ───────────────────────── Theme Five — Live Flash Sale ───────────────────────── */
2030 private function theme_five_design_fields() {
2031 return [
2032 // Container / overlay / close
2033 [ 'label' => __( 'Popup Max Width', 'notificationx' ), 'name' => 'exit_intent_t5_max_width', 'type' => 'number', 'default' => 900, 'description' => 'px' ],
2034 [ 'label' => __( 'Border Radius', 'notificationx' ), 'name' => 'exit_intent_t5_border_radius', 'type' => 'number', 'default' => 12, 'description' => 'px' ],
2035 [ 'label' => __( 'Left Panel Background', 'notificationx' ), 'name' => 'exit_intent_t5_bg_color', 'type' => 'colorpicker', 'default' => '#ffffff' ],
2036 [ 'label' => __( 'Overlay Background Color', 'notificationx' ), 'name' => 'exit_intent_overlay_color', 'type' => 'colorpicker', 'default' => 'rgba(0,0,0,0.6)' ],
2037 [ 'label' => __( 'Close Button Color', 'notificationx' ), 'name' => 'exit_intent_close_color', 'type' => 'colorpicker', 'default' => '#666666' ],
2038 [ 'label' => __( 'Close Button Size', 'notificationx' ), 'name' => 'exit_intent_close_size', 'type' => 'number', 'default' => 20, 'description' => 'px' ],
2039
2040 // Title
2041 [ 'label' => __( 'Title Color', 'notificationx' ), 'name' => 'exit_intent_t5_title_color', 'type' => 'colorpicker', 'default' => '#1a1a2e' ],
2042 [ 'label' => __( 'Title Font Size', 'notificationx' ), 'name' => 'exit_intent_t5_title_font_size', 'type' => 'number', 'default' => 18, 'description' => 'px' ],
2043 [ 'label' => __( 'Title Font Weight', 'notificationx' ), 'name' => 'exit_intent_t5_title_font_weight', 'type' => 'select', 'default' => '700',
2044 'options' => $this->font_weight_options() ],
2045
2046 // Headline
2047 [ 'label' => __( 'Headline Color', 'notificationx' ), 'name' => 'exit_intent_t5_headline_color', 'type' => 'colorpicker', 'default' => '#1a1a2e' ],
2048 [ 'label' => __( 'Headline Font Size', 'notificationx' ), 'name' => 'exit_intent_t5_headline_font_size', 'type' => 'number', 'default' => 56, 'description' => 'px' ],
2049 [ 'label' => __( 'Headline Font Weight', 'notificationx' ), 'name' => 'exit_intent_t5_headline_font_weight', 'type' => 'select', 'default' => '800',
2050 'options' => $this->font_weight_options() ],
2051
2052 // Description
2053 [ 'label' => __( 'Description Color', 'notificationx' ), 'name' => 'exit_intent_t5_desc_color', 'type' => 'colorpicker', 'default' => '#4a4a6a' ],
2054 [ 'label' => __( 'Description Font Size', 'notificationx' ), 'name' => 'exit_intent_t5_desc_font_size', 'type' => 'number', 'default' => 14, 'description' => 'px' ],
2055
2056 // Countdown label + numbers + unit labels
2057 [ 'label' => __( 'Countdown Label Color', 'notificationx' ), 'name' => 'exit_intent_t5_cd_label_color', 'type' => 'colorpicker', 'default' => '#1a1a2e',
2058 'rules' => Rules::is( 'exit_intent_t5_show_timer', true ) ],
2059 [ 'label' => __( 'Countdown Label Font Size', 'notificationx' ), 'name' => 'exit_intent_t5_cd_label_font_size', 'type' => 'number', 'default' => 12, 'description' => 'px',
2060 'rules' => Rules::is( 'exit_intent_t5_show_timer', true ) ],
2061 [ 'label' => __( 'Countdown Number Background', 'notificationx' ), 'name' => 'exit_intent_t5_cd_num_bg', 'type' => 'colorpicker', 'default' => '#fff0f5',
2062 'rules' => Rules::is( 'exit_intent_t5_show_timer', true ) ],
2063 [ 'label' => __( 'Countdown Number Color', 'notificationx' ), 'name' => 'exit_intent_t5_cd_num_color', 'type' => 'colorpicker', 'default' => '#e91e63',
2064 'rules' => Rules::is( 'exit_intent_t5_show_timer', true ) ],
2065 [ 'label' => __( 'Countdown Number Font Size', 'notificationx' ), 'name' => 'exit_intent_t5_cd_num_font_size', 'type' => 'number', 'default' => 22, 'description' => 'px',
2066 'rules' => Rules::is( 'exit_intent_t5_show_timer', true ) ],
2067 [ 'label' => __( 'Countdown Number Border Radius', 'notificationx' ),'name' => 'exit_intent_t5_cd_num_radius', 'type' => 'number', 'default' => 6, 'description' => 'px',
2068 'rules' => Rules::is( 'exit_intent_t5_show_timer', true ) ],
2069 [ 'label' => __( 'Countdown Unit Label Color', 'notificationx' ), 'name' => 'exit_intent_t5_cd_unit_color', 'type' => 'colorpicker', 'default' => '#4a4a6a',
2070 'rules' => Rules::is( 'exit_intent_t5_show_timer', true ) ],
2071 [ 'label' => __( 'Countdown Unit Label Font Size', 'notificationx' ),'name' => 'exit_intent_t5_cd_unit_font_size', 'type' => 'number', 'default' => 11, 'description' => 'px',
2072 'rules' => Rules::is( 'exit_intent_t5_show_timer', true ) ],
2073
2074 // CTA button
2075 [ 'label' => __( 'Button Background', 'notificationx' ), 'name' => 'exit_intent_t5_btn_bg', 'type' => 'colorpicker', 'default' => '#e91e63' ],
2076 [ 'label' => __( 'Button Text Color', 'notificationx' ), 'name' => 'exit_intent_t5_btn_color', 'type' => 'colorpicker', 'default' => '#ffffff' ],
2077 [ 'label' => __( 'Button Border Radius', 'notificationx' ), 'name' => 'exit_intent_t5_btn_border_radius', 'type' => 'number', 'default' => 6, 'description' => 'px' ],
2078 [ 'label' => __( 'Button Font Size', 'notificationx' ), 'name' => 'exit_intent_t5_btn_font_size', 'type' => 'number', 'default' => 14, 'description' => 'px' ],
2079 [ 'label' => __( 'Button Font Weight', 'notificationx' ), 'name' => 'exit_intent_t5_btn_font_weight', 'type' => 'select', 'default' => '700',
2080 'options' => $this->font_weight_options() ],
2081
2082 // Dismiss link
2083 [ 'label' => __( 'Dismiss Text Color', 'notificationx' ), 'name' => 'exit_intent_t5_dismiss_color', 'type' => 'colorpicker', 'default' => '#9a9aa8' ],
2084 [ 'label' => __( 'Dismiss Font Size', 'notificationx' ), 'name' => 'exit_intent_t5_dismiss_font_size', 'type' => 'number', 'default' => 12, 'description' => 'px' ],
2085 ];
2086 }
2087
2088 /** ───────────────────────── Theme Six — Product Countdown ───────────────────────── */
2089 private function theme_six_design_fields() {
2090 return [
2091 // Container / overlay / close
2092 [ 'label' => __( 'Popup Max Width', 'notificationx' ), 'name' => 'exit_intent_t6_max_width', 'type' => 'number', 'default' => 600, 'description' => 'px' ],
2093 [ 'label' => __( 'Border Radius', 'notificationx' ), 'name' => 'exit_intent_t6_border_radius', 'type' => 'number', 'default' => 12, 'description' => 'px' ],
2094 [ 'label' => __( 'Background Start Color', 'notificationx' ), 'name' => 'exit_intent_t6_bg_start', 'type' => 'colorpicker', 'default' => '#ffffff',
2095 'help' => __( 'Center color of the radial background gradient.', 'notificationx' ) ],
2096 [ 'label' => __( 'Background Mid Color', 'notificationx' ), 'name' => 'exit_intent_t6_bg_mid', 'type' => 'colorpicker', 'default' => '#fdf2f8' ],
2097 [ 'label' => __( 'Background End Color', 'notificationx' ), 'name' => 'exit_intent_t6_bg_end', 'type' => 'colorpicker', 'default' => '#f5f3ff',
2098 'help' => __( 'Outer color of the radial background gradient.', 'notificationx' ) ],
2099 [ 'label' => __( 'Overlay Background Color', 'notificationx' ), 'name' => 'exit_intent_overlay_color', 'type' => 'colorpicker', 'default' => 'rgba(0,0,0,0.5)' ],
2100 [ 'label' => __( 'Close Button Color', 'notificationx' ), 'name' => 'exit_intent_close_color', 'type' => 'colorpicker', 'default' => '#1f2937' ],
2101 [ 'label' => __( 'Close Button Size', 'notificationx' ), 'name' => 'exit_intent_close_size', 'type' => 'number', 'default' => 20, 'description' => 'px' ],
2102
2103 // Title
2104 [ 'label' => __( 'Title Color', 'notificationx' ), 'name' => 'exit_intent_t6_title_color', 'type' => 'colorpicker', 'default' => '#1f2937' ],
2105 [ 'label' => __( 'Title Font Size', 'notificationx' ), 'name' => 'exit_intent_t6_title_font_size', 'type' => 'number', 'default' => 36, 'description' => 'px' ],
2106 [ 'label' => __( 'Title Font Weight', 'notificationx' ), 'name' => 'exit_intent_t6_title_font_weight', 'type' => 'select', 'default' => '700',
2107 'options' => $this->font_weight_options() ],
2108
2109 // Countdown label + numbers + unit labels
2110 [ 'label' => __( 'Countdown Label Color', 'notificationx' ), 'name' => 'exit_intent_t6_cd_label_color', 'type' => 'colorpicker', 'default' => '#4b5563',
2111 'rules' => Rules::is( 'exit_intent_t6_show_timer', true ) ],
2112 [ 'label' => __( 'Countdown Label Font Size', 'notificationx' ), 'name' => 'exit_intent_t6_cd_label_font_size', 'type' => 'number', 'default' => 18, 'description' => 'px',
2113 'rules' => Rules::is( 'exit_intent_t6_show_timer', true ) ],
2114 [ 'label' => __( 'Countdown Number Background', 'notificationx' ), 'name' => 'exit_intent_t6_cd_num_bg', 'type' => 'colorpicker', 'default' => '#f3e8f2',
2115 'rules' => Rules::is( 'exit_intent_t6_show_timer', true ) ],
2116 [ 'label' => __( 'Countdown Number Color', 'notificationx' ), 'name' => 'exit_intent_t6_cd_num_color', 'type' => 'colorpicker', 'default' => '#374151',
2117 'rules' => Rules::is( 'exit_intent_t6_show_timer', true ) ],
2118 [ 'label' => __( 'Countdown Number Font Size', 'notificationx' ), 'name' => 'exit_intent_t6_cd_num_font_size', 'type' => 'number', 'default' => 24, 'description' => 'px',
2119 'rules' => Rules::is( 'exit_intent_t6_show_timer', true ) ],
2120 [ 'label' => __( 'Countdown Number Border Radius', 'notificationx' ),'name' => 'exit_intent_t6_cd_num_radius', 'type' => 'number', 'default' => 2, 'description' => 'px',
2121 'rules' => Rules::is( 'exit_intent_t6_show_timer', true ) ],
2122 [ 'label' => __( 'Countdown Unit Label Color', 'notificationx' ), 'name' => 'exit_intent_t6_cd_unit_color', 'type' => 'colorpicker', 'default' => '#9f1239',
2123 'rules' => Rules::is( 'exit_intent_t6_show_timer', true ) ],
2124 [ 'label' => __( 'Countdown Unit Label Font Size', 'notificationx' ),'name' => 'exit_intent_t6_cd_unit_font_size', 'type' => 'number', 'default' => 12, 'description' => 'px',
2125 'rules' => Rules::is( 'exit_intent_t6_show_timer', true ) ],
2126
2127 // CTA button
2128 [ 'label' => __( 'Button Background', 'notificationx' ), 'name' => 'exit_intent_t6_btn_bg', 'type' => 'colorpicker', 'default' => '#845e7c' ],
2129 [ 'label' => __( 'Button Text Color', 'notificationx' ), 'name' => 'exit_intent_t6_btn_color', 'type' => 'colorpicker', 'default' => '#ffffff' ],
2130 [ 'label' => __( 'Button Border Radius', 'notificationx' ), 'name' => 'exit_intent_t6_btn_border_radius', 'type' => 'number', 'default' => 4, 'description' => 'px' ],
2131 [ 'label' => __( 'Button Font Size', 'notificationx' ), 'name' => 'exit_intent_t6_btn_font_size', 'type' => 'number', 'default' => 24, 'description' => 'px' ],
2132 [ 'label' => __( 'Button Font Weight', 'notificationx' ), 'name' => 'exit_intent_t6_btn_font_weight', 'type' => 'select', 'default' => '700',
2133 'options' => $this->font_weight_options() ],
2134 ];
2135 }
2136
2137 /** ───────────────────────── Theme Seven — Lead Capture w/ Image ───────────────────────── */
2138 private function theme_seven_design_fields() {
2139 $font_family_options = GlobalFields::get_instance()->normalize_fields( [
2140 'inherit' => __( 'Default (inherit)', 'notificationx' ),
2141 "'Playfair Display', Georgia, serif" => __( 'Playfair Display (serif)', 'notificationx' ),
2142 "Georgia, 'Times New Roman', serif" => __( 'Georgia (serif)', 'notificationx' ),
2143 "'Times New Roman', Times, serif" => __( 'Times New Roman (serif)', 'notificationx' ),
2144 "Inter, 'Helvetica Neue', Arial, sans-serif" => __( 'Inter (sans-serif)', 'notificationx' ),
2145 "'Helvetica Neue', Helvetica, Arial, sans-serif" => __( 'Helvetica (sans-serif)', 'notificationx' ),
2146 ] );
2147
2148 return [
2149 // Container / overlay / close
2150 [ 'label' => __( 'Popup Max Width', 'notificationx' ), 'name' => 'exit_intent_t7_max_width', 'type' => 'number', 'default' => 750, 'description' => 'px' ],
2151 [ 'label' => __( 'Border Radius', 'notificationx' ), 'name' => 'exit_intent_t7_border_radius', 'type' => 'number', 'default' => 12, 'description' => 'px' ],
2152 [ 'label' => __( 'Background Color', 'notificationx' ), 'name' => 'exit_intent_t7_bg_color', 'type' => 'colorpicker', 'default' => '#413532',
2153 'help' => __( 'Right (content) panel background.', 'notificationx' ) ],
2154 [ 'label' => __( 'Image Panel Background', 'notificationx' ), 'name' => 'exit_intent_t7_image_bg', 'type' => 'colorpicker', 'default' => '#534542',
2155 'help' => __( 'Shown behind the image (visible only if the image leaves transparent areas or fails to load).', 'notificationx' ) ],
2156 [ 'label' => __( 'Overlay Background Color', 'notificationx' ), 'name' => 'exit_intent_overlay_color', 'type' => 'colorpicker', 'default' => 'rgba(0,0,0,0.5)' ],
2157 [ 'label' => __( 'Close Button Color', 'notificationx' ), 'name' => 'exit_intent_close_color', 'type' => 'colorpicker', 'default' => '#f3d5a2' ],
2158 [ 'label' => __( 'Close Button Size', 'notificationx' ), 'name' => 'exit_intent_close_size', 'type' => 'number', 'default' => 22, 'description' => 'px' ],
2159
2160 // Headline
2161 [ 'label' => __( 'Headline Color', 'notificationx' ), 'name' => 'exit_intent_t7_headline_color', 'type' => 'colorpicker', 'default' => '#f3d5a2' ],
2162 [ 'label' => __( 'Headline Font Size', 'notificationx' ), 'name' => 'exit_intent_t7_headline_font_size', 'type' => 'number', 'default' => 30, 'description' => 'px' ],
2163 [ 'label' => __( 'Headline Font Weight', 'notificationx' ), 'name' => 'exit_intent_t7_headline_font_weight', 'type' => 'select', 'default' => '700',
2164 'options' => $this->font_weight_options() ],
2165 [ 'label' => __( 'Headline Font Family', 'notificationx' ), 'name' => 'exit_intent_t7_headline_font_family', 'type' => 'select', 'default' => "'Playfair Display', Georgia, serif",
2166 'options' => $font_family_options ],
2167
2168 // Discount banner
2169 [ 'label' => __( 'Discount Banner Background', 'notificationx' ), 'name' => 'exit_intent_t7_discount_bg', 'type' => 'colorpicker', 'default' => 'rgba(255,255,255,0.1)' ],
2170 [ 'label' => __( 'Discount Banner Border Color', 'notificationx' ), 'name' => 'exit_intent_t7_discount_border', 'type' => 'colorpicker', 'default' => 'rgba(255,255,255,0.05)' ],
2171 [ 'label' => __( 'Discount Banner Text Color', 'notificationx' ), 'name' => 'exit_intent_t7_discount_color', 'type' => 'colorpicker', 'default' => '#f3d5a2' ],
2172 [ 'label' => __( 'Discount Banner Font Size', 'notificationx' ), 'name' => 'exit_intent_t7_discount_font_size', 'type' => 'number', 'default' => 16, 'description' => 'px' ],
2173 [ 'label' => __( 'Discount Banner Border Radius', 'notificationx' ), 'name' => 'exit_intent_t7_discount_radius', 'type' => 'number', 'default' => 2, 'description' => 'px' ],
2174
2175 // Description
2176 [ 'label' => __( 'Description Color', 'notificationx' ), 'name' => 'exit_intent_t7_desc_color', 'type' => 'colorpicker', 'default' => 'rgba(243,213,162,0.9)' ],
2177 [ 'label' => __( 'Description Font Size', 'notificationx' ), 'name' => 'exit_intent_t7_desc_font_size', 'type' => 'number', 'default' => 16, 'description' => 'px' ],
2178
2179 // Email input
2180 [ 'label' => __( 'Input Background Color', 'notificationx' ), 'name' => 'exit_intent_t7_input_bg', 'type' => 'colorpicker', 'default' => '#ffffff' ],
2181 [ 'label' => __( 'Input Border Color', 'notificationx' ), 'name' => 'exit_intent_t7_input_border_color', 'type' => 'colorpicker', 'default' => '#6b7280' ],
2182 [ 'label' => __( 'Input Border Radius', 'notificationx' ), 'name' => 'exit_intent_t7_input_border_radius','type' => 'number', 'default' => 0, 'description' => 'px' ],
2183 [ 'label' => __( 'Input Text Color', 'notificationx' ), 'name' => 'exit_intent_t7_input_text_color', 'type' => 'colorpicker', 'default' => '#1f2937' ],
2184
2185 // CTA button
2186 [ 'label' => __( 'Button Background', 'notificationx' ), 'name' => 'exit_intent_t7_btn_bg', 'type' => 'colorpicker', 'default' => '#f3d5a2' ],
2187 [ 'label' => __( 'Button Text Color', 'notificationx' ), 'name' => 'exit_intent_t7_btn_color', 'type' => 'colorpicker', 'default' => '#4d3e3e' ],
2188 [ 'label' => __( 'Button Border Radius', 'notificationx' ), 'name' => 'exit_intent_t7_btn_border_radius', 'type' => 'number', 'default' => 0, 'description' => 'px' ],
2189 [ 'label' => __( 'Button Font Size', 'notificationx' ), 'name' => 'exit_intent_t7_btn_font_size', 'type' => 'number', 'default' => 14, 'description' => 'px' ],
2190 [ 'label' => __( 'Button Font Weight', 'notificationx' ), 'name' => 'exit_intent_t7_btn_font_weight', 'type' => 'select', 'default' => '700',
2191 'options' => $this->font_weight_options() ],
2192 ];
2193 }
2194
2195 public function customize_fields( $fields ) {
2196 // Hide standard timing/behaviour irrelevant to exit intent
2197 foreach ( [ 'timing', 'behaviour', 'sound_section', 'queue_management', 'appearance', 'animation' ] as $key ) {
2198 if ( isset( $fields[ $key ] ) ) {
2199 $fields[ $key ] = Rules::is( 'source', $this->id, true, $fields[ $key ] );
2200 }
2201 }
2202
2203 // Add center position option
2204 if ( isset( $fields['appearance']['fields']['position']['options'] ) ) {
2205 $fields['appearance']['fields']['position']['options']['center'] = [
2206 'label' => __( 'Center', 'notificationx' ),
2207 'value' => 'center',
2208 'rules' => Rules::is( 'source', $this->id ),
2209 ];
2210 }
2211
2212 // NOTE: keep this key unique. The Announcement extension (PopupNotification)
2213 // also registers a section literally named `exit_intent_settings` for its
2214 // "Convert to Exit Intent" feature; sharing the key here let Popup's filter
2215 // (same `nx_customize_fields` priority, registered later) overwrite this
2216 // section with a `source == popup_notification` rule, hiding it for the
2217 // Exit Intent type and leaving the whole Customize tab blank.
2218 $fields['exit_intent_popup_settings'] = [
2219 'label' => __( 'Exit Intent Settings', 'notificationx' ),
2220 'name' => 'exit_intent_popup_settings',
2221 'type' => 'section',
2222 'priority' => 15,
2223 'rules' => Rules::is( 'source', $this->id ),
2224 'fields' => [
2225 'show_close_button' => [
2226 'label' => __( 'Show Close Button', 'notificationx' ),
2227 'name' => 'show_close_button',
2228 'type' => 'toggle',
2229 'default' => true,
2230 ],
2231 'exit_intent_position' => [
2232 'label' => __( 'Position', 'notificationx' ),
2233 'name' => 'exit_intent_position',
2234 'type' => 'select',
2235 'default' => 'center',
2236 'options' => GlobalFields::get_instance()->normalize_fields( [
2237 'center' => __( 'Center', 'notificationx' ),
2238 'bottom-left' => __( 'Bottom Left', 'notificationx' ),
2239 'bottom-right' => __( 'Bottom Right', 'notificationx' ),
2240 ] ),
2241 ],
2242 'exit_intent_sensitivity' => [
2243 'label' => __( 'Trigger Sensitivity', 'notificationx' ),
2244 'name' => 'exit_intent_sensitivity',
2245 'type' => 'select',
2246 'default' => '20',
2247 'help' => __( 'Distance from top of viewport (px) that triggers the popup.', 'notificationx' ),
2248 'options' => GlobalFields::get_instance()->normalize_fields( [
2249 '10' => __( 'High (10px)', 'notificationx' ),
2250 '20' => __( 'Medium (20px)', 'notificationx' ),
2251 '50' => __( 'Low (50px)', 'notificationx' ),
2252 ] ),
2253 ],
2254 'exit_intent_cookie_days' => [
2255 'label' => __( 'Do Not Show Again For', 'notificationx' ),
2256 'name' => 'exit_intent_cookie_days',
2257 'type' => 'number',
2258 'default' => 7,
2259 'description' => __( 'days', 'notificationx' ),
2260 'help' => __( 'Once dismissed, hide the popup for this many days.', 'notificationx' ),
2261 ],
2262 'exit_intent_mobile_disable' => [
2263 'label' => __( 'Disable on Mobile', 'notificationx' ),
2264 'name' => 'exit_intent_mobile_disable',
2265 'type' => 'toggle',
2266 'default' => true,
2267 'help' => __( 'Exit intent detection is unreliable on touch devices.', 'notificationx' ),
2268 ],
2269 ],
2270 ];
2271
2272 return $fields;
2273 }
2274
2275 public function doc() {
2276 /* translators: %1$s: documentation link URL */
2277 return sprintf(__('
2278 <p>Show a targeted message at the exact moment someone is about to close your tab & bring them back into the funnel. Need help? Check out our <a target="_blank" href="%1$s">documentation</a>.</p>',
2279 'notificationx'),
2280 'https://notificationx.com/docs/how-to-configure-exit-intent-popup/',
2281 );
2282 }
2283 }
2284