diff --git a/.windsurf/rules/formidable/frm-css.md b/.windsurf/rules/formidable/frm-css.md index 503c8ef4e9..1872a2cc0d 100644 --- a/.windsurf/rules/formidable/frm-css.md +++ b/.windsurf/rules/formidable/frm-css.md @@ -1,5 +1,5 @@ --- -trigger: "glob" +trigger: glob globs: ["**/*.css", "**/*.scss", "**/*.less"] description: "WordPress CSS coding standards with Formidable Forms patterns. Auto-applies when working with CSS files." --- diff --git a/classes/controllers/FrmFormActionsController.php b/classes/controllers/FrmFormActionsController.php index 05da258b6c..c71324e17d 100644 --- a/classes/controllers/FrmFormActionsController.php +++ b/classes/controllers/FrmFormActionsController.php @@ -56,25 +56,25 @@ public static function register_actions() { 'email' => 'FrmEmailAction', 'wppost' => 'FrmDefPostAction', 'register' => 'FrmDefRegAction', - 'paypal' => 'FrmDefPayPalAction', - 'payment' => 'FrmTransLiteAction', 'quiz' => 'FrmDefQuizAction', 'quiz_outcome' => 'FrmDefQuizOutcomeAction', - 'mailchimp' => 'FrmDefMlcmpAction', + 'paypal' => 'FrmDefPayPalAction', + 'payment' => 'FrmTransLiteAction', 'api' => 'FrmDefApiAction', - 'salesforce' => 'FrmDefSalesforceAction', + 'mailchimp' => 'FrmDefMlcmpAction', 'activecampaign' => 'FrmDefActiveCampaignAction', 'constantcontact' => 'FrmDefConstContactAction', 'getresponse' => 'FrmDefGetResponseAction', - 'hubspot' => 'FrmDefHubspotAction', - 'zapier' => 'FrmDefZapierAction', - 'n8n' => 'FrmDefN8NAction', - 'twilio' => 'FrmDefTwilioAction', - 'highrise' => 'FrmDefHighriseAction', 'mailpoet' => 'FrmDefMailpoetAction', - 'aweber' => 'FrmDefAweberAction', 'convertkit' => 'FrmDefConvertKitAction', + 'aweber' => 'FrmDefAweberAction', + 'twilio' => 'FrmDefTwilioAction', + 'salesforce' => 'FrmDefSalesforceAction', + 'hubspot' => 'FrmDefHubspotAction', + 'highrise' => 'FrmDefHighriseAction', + 'zapier' => 'FrmDefZapierAction', 'googlespreadsheet' => 'FrmDefGoogleSpreadsheetAction', + 'n8n' => 'FrmDefN8NAction', ); $action_classes = apply_filters( 'frm_registered_form_actions', $action_classes ); @@ -86,6 +86,50 @@ public static function register_actions() { foreach ( $action_classes as $action_class ) { self::$registered_actions->register( $action_class ); } + + self::apply_default_action_descriptions(); + } + + /** + * Sets default descriptions on registered actions from a central list. + * + * Keeps the description when an add-on replaces a base action class without its own. + * + * @since x.x + * + * @return void + */ + private static function apply_default_action_descriptions() { + $descriptions = array( + 'on_submit' => __( 'Success messages', 'formidable' ), + 'email' => __( 'Autoresponder alerts', 'formidable' ), + 'wppost' => __( 'Content publishing', 'formidable' ), + 'register' => __( 'Account creation', 'formidable' ), + 'payment' => __( 'Transaction alerts', 'formidable' ), + 'paypal' => __( 'Payment gateway', 'formidable' ), + 'quiz' => __( 'Automated grading', 'formidable' ), + 'quiz_outcome' => __( 'Result logic', 'formidable' ), + 'aweber' => __( 'List triggers', 'formidable' ), + 'mailchimp' => __( 'Subscription confirmation', 'formidable' ), + 'zapier' => __( 'App automation', 'formidable' ), + 'n8n' => __( 'Workflow automation', 'formidable' ), + 'twilio' => __( 'Text notifications', 'formidable' ), + 'activecampaign' => __( 'Contact automation', 'formidable' ), + 'salesforce' => __( 'Lead automation', 'formidable' ), + 'constantcontact' => __( 'Content distribution', 'formidable' ), + 'getresponse' => __( 'Success notifications', 'formidable' ), + 'hubspot' => __( 'CRM alerts', 'formidable' ), + 'mailpoet' => __( 'Plugin automation', 'formidable' ), + 'api' => __( 'System integration', 'formidable' ), + 'googlespreadsheet' => __( 'Spreadsheet sync', 'formidable' ), + 'convertkit' => __( 'Broadcast publishing', 'formidable' ), + ); + + foreach ( self::$registered_actions->actions as $action ) { + if ( $action->action_options['description'] === '' && isset( $descriptions[ $action->id_base ] ) ) { + $action->action_options['description'] = $descriptions[ $action->id_base ]; + } + } } /** @@ -170,16 +214,19 @@ public static function form_action_groups() { 'name' => '', 'icon' => 'frmfont frm_shuffle_icon', 'actions' => array( + 'on_submit', 'email', 'wppost', 'register', 'quiz', 'quiz_outcome', - 'twilio', + 'api', + 'googlespreadsheet', + 'n8n', ), ), 'payment' => array( - 'name' => __( 'eCommerce', 'formidable' ), + 'name' => __( 'E-Commerce', 'formidable' ), 'icon' => 'frmfont frm_credit_card_alt_icon', 'actions' => array( 'paypal', @@ -187,7 +234,7 @@ public static function form_action_groups() { ), ), 'marketing' => array( - 'name' => __( 'Email Marketing', 'formidable' ), + 'name' => __( 'Marketing', 'formidable' ), 'icon' => 'frmfont frm_mail_bulk_icon', 'actions' => array( 'mailchimp', @@ -197,6 +244,7 @@ public static function form_action_groups() { 'aweber', 'mailpoet', 'convertkit', + 'twilio', ), ), 'crm' => array( @@ -220,6 +268,7 @@ private static function get_crm_actions() { $crm_actions = array( 'salesforce', 'hubspot', + 'zapier', ); // Only include Highrise when the add-on is active. @@ -302,16 +351,26 @@ public static function show_action_icon_link( $action_control, $allowed ) { } }//end if - // HTML to include on the icon. - $icon_atts = array(); + include FrmAppHelper::plugin_path() . '/classes/views/frm-form-actions/_action_icon.php'; + } - if ( $action_control->action_options['color'] !== 'var(--primary-700)' ) { - $icon_atts = array( + /** + * Get the HTML attributes for the action icon. + * + * @since x.x + * + * @param object $action_control + * + * @return array + */ + public static function get_action_icon_atts( $action_control ) { + if ( 'var(--primary-700)' !== $action_control->action_options['color'] ) { + return array( 'style' => '--primary-700:' . $action_control->action_options['color'], ); } - include FrmAppHelper::plugin_path() . '/classes/views/frm-form-actions/_action_icon.php'; + return array(); } /** @@ -380,6 +439,10 @@ public static function list_actions( $form, $values ) { self::maybe_show_limit_warning( $form->id, $form_actions ); + echo '

' + . esc_html__( 'No actions have been added yet. Select an action above to get started.', 'formidable' ) + . '

'; + foreach ( $form_actions as $action ) { if ( ! isset( $action_map[ $action->post_excerpt ] ) ) { // Don't try and show settings if action no longer exists @@ -459,8 +522,14 @@ public static function add_form_action() { $action_control = self::get_form_actions( $action_type ); $action_control->_set( $action_key ); - $form_id = FrmAppHelper::get_param( 'form_id', '', 'post', 'absint' ); - $form_action = $action_control->prepare_new( $form_id ); + $form_id = FrmAppHelper::get_param( 'form_id', '', 'post', 'absint' ); + $form_action = $action_control->prepare_new( $form_id ); + $existing_titles = (array) FrmAppHelper::get_post_param( 'existing_titles', array(), 'sanitize_text_field' ); + + if ( $existing_titles ) { + $form_action->post_title = self::get_unique_action_title( $form_action->post_title, $existing_titles ); + } + $use_logging = self::should_show_log_message( $action_type ); $values = array(); $form = self::fields_to_values( $form_id, $values ); @@ -469,6 +538,27 @@ public static function add_form_action() { wp_die(); } + /** + * Returns the first available title not in $existing_titles, appending " (2)", " (3)", etc. if needed. + * + * @since x.x + * + * @param string $base_title Default action title from the action type. + * @param string[] $existing_titles Titles currently visible in the form editor. + * + * @return string + */ + private static function get_unique_action_title( $base_title, array $existing_titles ) { + $taken = array_flip( $existing_titles ); + $title = $base_title; + + for ( $n = 2; isset( $taken[ $title ] ); $n++ ) { + $title = $base_title . ' (' . $n . ')'; + } + + return $title; + } + public static function fill_action() { FrmAppHelper::permission_check( 'frm_edit_forms' ); check_ajax_referer( 'frm_ajax', 'nonce' ); diff --git a/classes/controllers/FrmFormsController.php b/classes/controllers/FrmFormsController.php index bc6799e307..b10905836d 100644 --- a/classes/controllers/FrmFormsController.php +++ b/classes/controllers/FrmFormsController.php @@ -1522,17 +1522,17 @@ private static function get_settings_tabs( $values ) { 'name' => __( 'General', 'formidable' ), 'title' => __( 'General Form Settings', 'formidable' ), 'function' => array( self::class, 'advanced_settings' ), - 'icon' => 'frmfont frm_settings_icon', + 'icon' => 'frmfont frm_small_settings_icon', ), 'email' => array( 'name' => __( 'Actions & Notifications', 'formidable' ), 'function' => array( 'FrmFormActionsController', 'email_settings' ), 'id' => 'frm_notification_settings', - 'icon' => 'frmfont frm_mail_bulk_icon', + 'icon' => 'frmfont frm_notification_check_icon', ), 'permissions' => array( 'name' => __( 'Form Permissions', 'formidable' ), - 'icon' => 'frmfont frm_lock_closed_icon', + 'icon' => 'frmfont frm_lock_closed2_icon', 'html_class' => 'frm_show_upgrade_tab frm_noallow', 'data' => array( 'medium' => 'permissions', @@ -1544,7 +1544,7 @@ private static function get_settings_tabs( $values ) { ), 'scheduling' => array( 'name' => __( 'Form Scheduling', 'formidable' ), - 'icon' => 'frmfont frm_calendar_icon', + 'icon' => 'frmfont frm_schedule_icon', 'html_class' => 'frm_show_upgrade_tab frm_noallow', 'data' => array( 'medium' => 'scheduling', @@ -1557,17 +1557,17 @@ private static function get_settings_tabs( $values ) { 'name' => __( 'Buttons', 'formidable' ), 'class' => self::class, 'function' => 'buttons_settings', - 'icon' => 'frmfont frm_button_icon', + 'icon' => 'frmfont frm-buttons-style', ), 'landing' => array( 'name' => __( 'Form Landing Page', 'formidable' ), - 'icon' => 'frmfont frm_file_text_icon', + 'icon' => 'frmfont frm_cross_device_icon', 'html_class' => 'frm_show_upgrade_tab frm_noallow', 'data' => FrmAppHelper::get_landing_page_upgrade_data_params(), ), 'chat' => array( 'name' => __( 'Conversational Forms', 'formidable' ), - 'icon' => 'frmfont frm_chat_forms_icon', + 'icon' => 'frmfont frm_chat_bubbles_icon', 'html_class' => 'frm_show_upgrade_tab frm_noallow', 'data' => FrmAppHelper::get_upgrade_data_params( 'chat', @@ -1597,7 +1597,7 @@ private static function get_settings_tabs( $values ) { 'name' => __( 'Customize HTML', 'formidable' ), 'class' => self::class, 'function' => 'html_settings', - 'icon' => 'frmfont frm_code_icon', + 'icon' => 'frmfont frm_code2_icon', ), ); diff --git a/classes/helpers/FrmAddonsHelper.php b/classes/helpers/FrmAddonsHelper.php index 06f67455f9..5ff9790ab8 100644 --- a/classes/helpers/FrmAddonsHelper.php +++ b/classes/helpers/FrmAddonsHelper.php @@ -232,7 +232,7 @@ public static function add_addon_attributes( $addon ) { * @return string */ private static function prepare_single_addon_classes( $addon ) { - $class_names = array( 'frm-card-item frm-flex-col' ); + $class_names = array( 'frm-card-item frm-card-item--outlined frm-flex-col' ); $class_names[] = 'plugin-card-' . $addon['slug']; $class_names[] = 'frm-addon-' . $addon['status']['type']; diff --git a/classes/models/FrmFormAction.php b/classes/models/FrmFormAction.php index 0f0ec7d1f7..0338ba72c4 100644 --- a/classes/models/FrmFormAction.php +++ b/classes/models/FrmFormAction.php @@ -158,6 +158,8 @@ public function __construct( $id_base, $name, $action_options = array(), $contro 'group' => $id_base, 'color' => '', 'keywords' => '', + 'description' => '', + 'is_new' => false, ); $action_options = apply_filters( 'frm_' . $id_base . '_action_options', $action_options ); @@ -968,9 +970,10 @@ public static function action_conditions_met( $action, $entry ) { */ public static function default_action_opts( $class = '' ) { return array( - 'classes' => 'frmfont ' . $class, - 'active' => false, - 'limit' => 0, + 'classes' => 'frmfont ' . $class, + 'active' => false, + 'limit' => 0, + 'description' => '', ); } diff --git a/classes/models/FrmOnSubmitAction.php b/classes/models/FrmOnSubmitAction.php index afba42dc46..07374bb489 100644 --- a/classes/models/FrmOnSubmitAction.php +++ b/classes/models/FrmOnSubmitAction.php @@ -20,13 +20,13 @@ class FrmOnSubmitAction extends FrmFormAction { public function __construct() { $action_ops = array( - 'classes' => 'frmfont frm_checkmark_icon', - 'active' => true, - 'event' => array( 'create' ), - 'limit' => 99, - 'priority' => 9, - 'color' => 'rgb(66, 193, 178)', - 'keywords' => __( 'redirect, success, confirmation, submit', 'formidable' ), + 'classes' => 'frmfont frm_checkmark_circle_icon', + 'active' => true, + 'event' => array( 'create' ), + 'limit' => 99, + 'priority' => 9, + 'color' => '#42C1B2', + 'keywords' => __( 'redirect, success, confirmation, submit', 'formidable' ), ); $action_ops = apply_filters( 'frm_' . self::$slug . '_control_settings', $action_ops ); diff --git a/classes/views/frm-form-actions/_action_icon.php b/classes/views/frm-form-actions/_action_icon.php index 2a508718d8..d2e8f66933 100644 --- a/classes/views/frm-form-actions/_action_icon.php +++ b/classes/views/frm-form-actions/_action_icon.php @@ -2,28 +2,47 @@ if ( ! defined( 'ABSPATH' ) ) { die( 'You are not allowed to call this page directly.' ); } + +$single_action_attrs = array_merge( + $data, + array( + 'href' => 'javascript:void(0)', + 'class' => $classes . ' button frm-button-secondary frm-button-sm frm-with-icon frm-ml-auto-force frm-fadein-down-short', + 'data-limit' => $action_control->action_options['limit'], + 'data-actiontype' => $action_control->id_base, + ) +); ?> -
  • - - > - - > - action_options['classes'], $icon_atts ); ?> - +
  • +
    + + action_options['classes'], FrmFormActionsController::get_action_icon_atts( $action_control ) ); ?> - name ) ); ?> - action_options['keywords'] ) ) { ?> - + +
    +

    action_options['keywords'] ); + if ( isset( $data['data-upgrade'] ) && ! isset( $data['data-oneclick'] ) ) { + FrmAppHelper::icon_by_class( 'frmfont frm_lock_icon frm_svg15', array( 'aria-label' => __( 'Lock icon', 'formidable' ) ) ); + } ?> - + name ) ); ?> + action_options['is_new'] ) ) { ?> + + +

    + action_options['description'] ) ) { ?> +

    action_options['description'] ); ?>

    + +
    + +
    > + + + + + action_options['keywords'] ) ) { ?> + action_options['keywords'] ); ?> - +
  • diff --git a/classes/views/frm-form-actions/default_actions.php b/classes/views/frm-form-actions/default_actions.php index a89edaf403..fb714f8ef8 100644 --- a/classes/views/frm-form-actions/default_actions.php +++ b/classes/views/frm-form-actions/default_actions.php @@ -8,8 +8,8 @@ */ class FrmDefPostAction extends FrmFormAction { public function __construct() { - $action_ops = FrmFormAction::default_action_opts( 'frm_wordpress_icon frm-inverse frm_show_upgrade' ); - $action_ops['color'] = 'rgb(0,160,210)'; + $action_ops = FrmFormAction::default_action_opts( 'frm_wordpress_icon frm_show_upgrade' ); + $action_ops['color'] = '#3177C7'; parent::__construct( 'wppost', __( 'Create Post', 'formidable' ), $action_ops ); } @@ -22,7 +22,7 @@ class FrmDefRegAction extends FrmFormAction { public function __construct() { $action_ops = FrmFormAction::default_action_opts( 'frm_register_icon frm_show_upgrade' ); $action_ops['plugin'] = 'registration'; - $action_ops['color'] = 'var(--pink)'; + $action_ops['color'] = '#e22a6e'; parent::__construct( 'register', __( 'Register User', 'formidable' ), $action_ops ); } } @@ -32,8 +32,8 @@ public function __construct() { */ class FrmDefPayPalAction extends FrmFormAction { public function __construct() { - $action_ops = FrmFormAction::default_action_opts( 'frm_paypal_icon frm-inverse frm_show_upgrade' ); - $action_ops['color'] = 'var(--primary-700)'; + $action_ops = FrmFormAction::default_action_opts( 'frm_paypal_icon frm_show_upgrade' ); + $action_ops['color'] = '#001c64'; parent::__construct( 'paypal', 'PayPal', $action_ops ); } @@ -44,8 +44,9 @@ public function __construct() { */ class FrmDefQuizAction extends FrmFormAction { public function __construct() { - $action_ops = FrmFormAction::default_action_opts( 'frm_percent_icon frm_quiz_icon frm_show_upgrade' ); + $action_ops = FrmFormAction::default_action_opts( 'frm_quiz_icon frm_show_upgrade' ); $action_ops['plugin'] = 'quizzes'; + $action_ops['color'] = '#f15a24'; parent::__construct( 'quiz', __( 'Scored Quiz', 'formidable' ), $action_ops ); } } @@ -55,8 +56,9 @@ public function __construct() { */ class FrmDefQuizOutcomeAction extends FrmFormAction { public function __construct() { - $action_ops = FrmFormAction::default_action_opts( 'frm_check1_icon frm_quiz_icon frm_show_upgrade' ); + $action_ops = FrmFormAction::default_action_opts( 'frm_quiz_outcome_icon frm_show_upgrade' ); $action_ops['plugin'] = 'quizzes'; + $action_ops['color'] = '#8d35f5'; parent::__construct( 'quiz_outcome', __( 'Quiz Outcome', 'formidable' ), $action_ops ); } } @@ -67,7 +69,7 @@ public function __construct() { class FrmDefAweberAction extends FrmFormAction { public function __construct() { $action_ops = FrmFormAction::default_action_opts( 'frm_aweber_icon frm_show_upgrade' ); - $action_ops['color'] = 'var(--green)'; + $action_ops['color'] = '#246BE8'; parent::__construct( 'aweber', 'AWeber', $action_ops ); } } @@ -77,8 +79,8 @@ public function __construct() { */ class FrmDefMlcmpAction extends FrmFormAction { public function __construct() { - $action_ops = FrmFormAction::default_action_opts( 'frm_mailchimp_icon frm_show_upgrade frm-inverse' ); - $action_ops['color'] = 'var(--grey-700)'; + $action_ops = FrmFormAction::default_action_opts( 'frm_mailchimp_icon frm_show_upgrade' ); + $action_ops['color'] = '#000'; parent::__construct( 'mailchimp', 'Mailchimp', $action_ops ); } @@ -90,7 +92,7 @@ public function __construct() { class FrmDefZapierAction extends FrmFormAction { public function __construct() { $action_ops = FrmFormAction::default_action_opts( 'frm_zapier_icon frm_show_upgrade' ); - $action_ops['color'] = 'var(--orange)'; + $action_ops['color'] = '#FF4A00'; parent::__construct( 'zapier', 'Zapier', $action_ops ); } } @@ -100,8 +102,9 @@ public function __construct() { */ class FrmDefN8NAction extends FrmFormAction { public function __construct() { - $action_ops = FrmFormAction::default_action_opts( 'frm_n8n_icon frm_show_upgrade' ); - $action_ops['color'] = '#EA4B71'; + $action_ops = FrmFormAction::default_action_opts( 'frm_n8n_icon frm_show_upgrade' ); + $action_ops['color'] = '#EA4B71'; + $action_ops['is_new'] = true; parent::__construct( 'n8n', 'n8n', $action_ops ); } } @@ -111,7 +114,8 @@ public function __construct() { */ class FrmDefTwilioAction extends FrmFormAction { public function __construct() { - $action_ops = FrmFormAction::default_action_opts( 'frm_sms_icon frm_show_upgrade' ); + $action_ops = FrmFormAction::default_action_opts( 'frm_sms_icon frm_show_upgrade' ); + $action_ops['color'] = '#F12E45'; parent::__construct( 'twilio', __( 'Twilio SMS', 'formidable' ), $action_ops ); } } @@ -119,15 +123,15 @@ public function __construct() { class FrmDefActiveCampaignAction extends FrmFormAction { public function __construct() { $action_ops = FrmFormAction::default_action_opts( 'frm_activecampaign_icon frm_show_upgrade' ); - $action_ops['color'] = 'var(--primary-700)'; + $action_ops['color'] = '#004CFF'; parent::__construct( 'activecampaign', 'ActiveCampaign', $action_ops ); } } class FrmDefSalesforceAction extends FrmFormAction { public function __construct() { - $action_ops = FrmFormAction::default_action_opts( 'frm_salesforce_icon frm-inverse frm_show_upgrade' ); - $action_ops['color'] = 'var(--primary-500)'; + $action_ops = FrmFormAction::default_action_opts( 'frm_salesforcealt_icon frm_show_upgrade' ); + $action_ops['color'] = '#00A1E0'; parent::__construct( 'salesforce', 'Salesforce', $action_ops ); } } @@ -135,7 +139,7 @@ public function __construct() { class FrmDefConstContactAction extends FrmFormAction { public function __construct() { $action_ops = FrmFormAction::default_action_opts( 'frm_constant_contact_icon frm_show_upgrade' ); - $action_ops['color'] = 'rgb(0,160,210)'; + $action_ops['color'] = '#1856ED'; parent::__construct( 'constantcontact', 'Constant Contact', $action_ops ); } } @@ -143,7 +147,7 @@ public function __construct() { class FrmDefGetResponseAction extends FrmFormAction { public function __construct() { $action_ops = FrmFormAction::default_action_opts( 'frm_getresponse_icon frm_show_upgrade' ); - $action_ops['color'] = '#00baff'; + $action_ops['color'] = '#00A2FF'; parent::__construct( 'getresponse', 'GetResponse', $action_ops ); } } @@ -151,7 +155,7 @@ public function __construct() { class FrmDefHubspotAction extends FrmFormAction { public function __construct() { $action_ops = FrmFormAction::default_action_opts( 'frm_hubspot_icon frm_show_upgrade' ); - $action_ops['color'] = 'var(--orange)'; + $action_ops['color'] = '#FF7A59'; $action_ops['message'] = ''; @@ -168,7 +172,7 @@ public function __construct() { class FrmDefMailpoetAction extends FrmFormAction { public function __construct() { $action_ops = FrmFormAction::default_action_opts( 'frm_mailpoet_icon frm_show_upgrade' ); - $action_ops['color'] = 'var(--orange)'; + $action_ops['color'] = '#FE5301'; parent::__construct( 'mailpoet', 'MailPoet', $action_ops ); } } @@ -176,7 +180,7 @@ public function __construct() { class FrmDefApiAction extends FrmFormAction { public function __construct() { $action_ops = FrmFormAction::default_action_opts( 'frm_feed_icon frm_show_upgrade' ); - $action_ops['color'] = 'var(--purple)'; + $action_ops['color'] = '#0193d7'; parent::__construct( 'api', __( 'Send API data', 'formidable' ), $action_ops ); } } @@ -187,7 +191,7 @@ public function __construct() { class FrmDefGoogleSpreadsheetAction extends FrmFormAction { public function __construct() { $action_ops = FrmFormAction::default_action_opts( 'frm_googlesheets_icon frm_show_upgrade' ); - $action_ops['color'] = 'var(--green)'; + $action_ops['color'] = '#0F9D58'; parent::__construct( 'googlespreadsheet', __( 'Google Sheets', 'formidable' ), $action_ops ); } } @@ -205,8 +209,8 @@ public function __construct() { class FrmDefConvertKitAction extends FrmFormAction { public function __construct() { - $action_ops = FrmFormAction::default_action_opts( 'frm_convertkit_icon frm-inverse frm_show_upgrade' ); - $action_ops['color'] = 'rgb(68 177 255)'; + $action_ops = FrmFormAction::default_action_opts( 'frm_convertkit_icon frm_show_upgrade' ); + $action_ops['color'] = '#1E1E1E'; parent::__construct( 'convertkit', 'Kit', $action_ops ); } } diff --git a/classes/views/frm-form-actions/email_action.php b/classes/views/frm-form-actions/email_action.php index 3b901f11a1..344ca8d4e1 100644 --- a/classes/views/frm-form-actions/email_action.php +++ b/classes/views/frm-form-actions/email_action.php @@ -7,12 +7,12 @@ class FrmEmailAction extends FrmFormAction { public function __construct() { $action_ops = array( - 'classes' => 'frmfont frm_email_solid_icon', - 'active' => true, - 'event' => array( 'create' ), - 'limit' => 99, - 'priority' => 10, - 'color' => 'rgb(49, 119, 199)', + 'classes' => 'frmfont frm_email_solid_icon', + 'active' => true, + 'event' => array( 'create' ), + 'limit' => 99, + 'priority' => 10, + 'color' => '#3177C7', ); $action_ops = apply_filters( 'frm_email_control_settings', $action_ops ); diff --git a/classes/views/frm-form-actions/form_action.php b/classes/views/frm-form-actions/form_action.php index d6ab47c0d6..80d8c1c056 100644 --- a/classes/views/frm-form-actions/form_action.php +++ b/classes/views/frm-form-actions/form_action.php @@ -20,53 +20,57 @@ class="widget frm_form_action_settings frm_single_post_excerpt ); ?>_settings post_status === 'publish' ? '' : 'frm_disabled_action' ); ?>" > -
    -
    - +
    +
    +

    + action_options['classes'], FrmFormActionsController::get_action_icon_atts( $action_control ) ); ?> + post_title ); ?> +

    - - action_options['limit'] > 2 ) { ?> - - + +
    + + action_options['limit'] > 2 ) { ?> + + + + + + + - - - - - get_field_id( 'post_status', '' ), - $action_control->get_field_name( 'post_status', '' ), - array( - 'checked' => $form_action->post_status === 'publish', - 'on_label' => 'publish', - 'off_label' => 'OFF', - 'show_labels' => false, - 'echo' => true, - ) - ); - ?> - -
    -

    - - action_options['classes'] ); ?> - - post_title ); ?> -

    + get_field_id( 'post_status', '' ), + $action_control->get_field_name( 'post_status', '' ), + array( + 'checked' => $form_action->post_status === 'publish', + 'on_label' => 'publish', + 'off_label' => 'OFF', + 'show_labels' => false, + 'echo' => true, + 'div_class' => 'frm-ml-xs', + ) + ); + ?> + + +
    + +
    +
    + ?> diff --git a/classes/views/frm-form-actions/settings.php b/classes/views/frm-form-actions/settings.php index 2d55a39a1d..215f02f7fa 100644 --- a/classes/views/frm-form-actions/settings.php +++ b/classes/views/frm-form-actions/settings.php @@ -2,93 +2,134 @@ if ( ! defined( 'ABSPATH' ) ) { die( 'You are not allowed to call this page directly.' ); } + +$single_action_attrs = array( + 'href' => 'javascript:void(0)', + 'class' => 'frm_single_action frm_inactive_action frm_show_upgrade button frm-button-secondary frm-button-sm frm-with-icon frm-ml-auto-force frm-fadein-down-short', +); ?>

    -
    - 'actions', - 'placeholder' => __( 'Search Form Actions', 'formidable' ), - 'tosearch' => 'frm-action', - ) - ); - ?> -

    - - - - -

    - -

    - +
    +
    +
    +
    +
      +
    • +
    • + $group ) { ?> + +
    • + + +
    +
    +
    - if ( ! isset( $group['actions'] ) ) { - $group['actions'] = array(); - } + 'actions', + 'placeholder' => __( 'Search Actions', 'formidable' ), + 'tosearch' => 'frm-action', + 'class' => 'frm-ml-auto-force', + ) + ); ?> -
      - id_base, $displayed_actions, true ) || ! in_array( $action_control->id_base, $group['actions'], true ) ) { - continue; - } +
    + +
    + id_base; - FrmFormActionsController::show_action_icon_link( $action_control, $allowed ); - } + foreach ( $groups as $group_key => $group ) { + ?> +
    + +

    + - foreach ( $group['actions'] as $action ) { - if ( in_array( $action, $displayed_actions, true ) ) { - continue; + -
  • - - - > - '--primary-700:' . $group['color'], - ); - } - FrmAppHelper::icon_by_class( 'frmfont frm_plus_icon', $icon_atts ); - ?> - - - - -
  • - - +
      + id_base, $displayed_actions, true ) || ! in_array( $action_control->id_base, $group['actions'], true ) ) { + continue; + } + + $displayed_actions[] = $action_control->id_base; + FrmFormActionsController::show_action_icon_link( $action_control, $allowed ); + } + + foreach ( $group['actions'] as $action ) { + if ( in_array( $action, $displayed_actions, true ) ) { + continue; + } + + $icon_atts = array(); + if ( isset( $group['color'] ) ) { + $icon_atts = array( + 'style' => '--primary-700:' . $group['color'], + ); + } + + $action_icon = isset( $group['icon'] ) ? $group['icon'] : 'frmfont frm_plus_icon'; + + $single_action_attrs['data-upgrade'] = sprintf( + /* translators: %s: Name of form action */ + __( '%s form actions', 'formidable' ), + $action + ); + $single_action_attrs['data-medium'] = 'settings-' . $action; + ?> +
    • +
      + + + + +
      +

      + __( 'Lock icon', 'formidable' ) ) ); ?> + + +

      +
      + + > + + + +
      +
    • + +
    +
    -
    - - - - - - -
    + }//end foreach + ?> + +

    + +

    +
    +

    + > 'true' ) ); ?> - + diff --git a/css/admin/addons-page.css b/css/admin/addons-page.css index 0e03db3387..4f42f58334 100644 --- a/css/admin/addons-page.css +++ b/css/admin/addons-page.css @@ -23,23 +23,6 @@ } /* Card */ -#frm-addons-page .frm-card-item { - padding: var(--gap-sm) var(--gap-md); - gap: 12px; - border-color: var(--grey-300); - box-shadow: var(--box-shadow-sm); - transition: box-shadow 200ms ease-in-out; -} - -#frm-addons-page .frm-card-item:hover { - box-shadow: var(--box-shadow-md); -} - -.frm-card-item p, -.frm-card-item h3 { - margin: 0; -} - .frm-border-icon > svg { width: 24px; } diff --git a/css/admin/animations.css b/css/admin/animations.css index 2942a90817..507f4187b3 100644 --- a/css/admin/animations.css +++ b/css/admin/animations.css @@ -1,6 +1,7 @@ /* fadeIn */ .frm-fadein { - animation: fadeIn 400ms ease-in-out forwards; + --frm-fadein-time: 400ms; + animation: fadeIn var(--frm-fadein-time, 400ms) ease-in-out forwards; } .frm-fadein-up { @@ -12,7 +13,8 @@ } .frm-fadein-down-short { - animation: fadeInDownShort 300ms ease-out forwards; + --frm-fadein-down-short-time: 300ms; + animation: fadeInDownShort var(--frm-fadein-down-short-time, 300ms) ease-out forwards; } .frm-fadeout { diff --git a/images/icons.svg b/images/icons.svg index e5c8955e0f..97aae859bc 100644 --- a/images/icons.svg +++ b/images/icons.svg @@ -1,7 +1,7 @@ diff --git a/js/src/admin/admin.js b/js/src/admin/admin.js index c3d6ccec78..bf0a6cc11f 100644 --- a/js/src/admin/admin.js +++ b/js/src/admin/admin.js @@ -639,9 +639,25 @@ window.frmAdminBuildJS = function() { return false; } + /** + * Updates the empty state of the actions search results. + * + * @since x.x + */ + function updateActionsSearchEmptyState() { + document.getElementById( 'frm-actions-no-results' )?.classList.toggle( + 'frm_hidden', + document.querySelector( '#frm-actions-filter-content .frm-action:not(.frm_hidden)' ) + ); + } + function afterActionRemoved( type ) { checkActiveAction( type ); + if ( ! document.querySelector( '.frm_form_action_settings' ) ) { + document.querySelector( '.frm-no-actions-message' )?.classList.remove( 'frm_hidden' ); + } + const hookName = 'frm_after_action_removed'; const hookArgs = { type }; wp.hooks.doAction( hookName, hookArgs ); @@ -7471,6 +7487,10 @@ window.frmAdminBuildJS = function() { const actionId = getNewActionId(); const formId = thisFormId; + const existingTitles = Array.from( + document.querySelectorAll( `.frm_single_${ type }_settings .widget-title h4 span` ), + el => el.textContent.trim() + ); const placeholderSetting = document.createElement( 'div' ); placeholderSetting.classList.add( `frm_single_${ type }_settings` ); @@ -7486,7 +7506,8 @@ window.frmAdminBuildJS = function() { type, list_id: actionId, form_id: formId, - nonce: frmGlobal.nonce + nonce: frmGlobal.nonce, + existing_titles: existingTitles }, success: handleAddFormActionSuccess } ); @@ -7495,6 +7516,8 @@ window.frmAdminBuildJS = function() { fieldUpdated(); placeholderSetting.remove(); + document.querySelector( '.frm-no-actions-message' )?.classList.add( 'frm_hidden' ); + closeOpenActions(); const newActionContainer = div(); @@ -7542,24 +7565,6 @@ window.frmAdminBuildJS = function() { ); } - function toggleActionGroups() { - /*jshint validthis:true */ - const actions = document.getElementById( 'frm_email_addon_menu' ).classList; - const search = document.getElementById( 'actions-search-input' ); - - if ( actions.contains( 'frm-all-actions' ) ) { - actions.remove( 'frm-all-actions' ); - actions.add( 'frm-limited-actions' ); - } else { - actions.add( 'frm-all-actions' ); - actions.remove( 'frm-limited-actions' ); - } - - // Reset search. - search.value = ''; - triggerEvent( search, 'input' ); - } - function getNewActionId() { const actionSettings = document.querySelectorAll( '.frm_form_action_settings' ); let len = getNewRowId( actionSettings, 'frm_form_action_' ); @@ -9403,12 +9408,6 @@ window.frmAdminBuildJS = function() { regEx = true; } - if ( toSearch === 'frm-action' && searchText !== '' ) { - const addons = document.getElementById( 'frm_email_addon_menu' ).classList; - addons.remove( 'frm-all-actions' ); - addons.add( 'frm-limited-actions' ); - } - for ( i = 0; i < items.length; i++ ) { const innerText = items[ i ].innerText.toLowerCase(); @@ -10629,20 +10628,9 @@ window.frmAdminBuildJS = function() { $formActions.on( 'click', '.frm_toggle_cf_opts', toggleCfOpts ); $formActions.on( 'click', '.frm_duplicate_form_action', copyFormAction ); jQuery( '.frm_actions_list' ).on( 'click', '.frm_active_action', addFormAction ); - jQuery( '#frm-show-groups, #frm-hide-groups' ).on( 'click', toggleActionGroups ); + jQuery( document ).on( 'frmAfterSearch', '#actions-search-input', updateActionsSearchEmptyState ); initiateMultiselect(); - //set actions icons to inactive - jQuery( 'ul.frm_actions_list li' ).each( function() { - checkActiveAction( jQuery( this ).children( 'a' ).data( 'actiontype' ) ); - - // If the icon is a background image, don't add BG color. - const icon = jQuery( this ).find( 'i' ); - if ( icon.css( 'background-image' ) !== 'none' ) { - icon.addClass( 'frm-inverse' ); - } - } ); - jQuery( '.frm_submit_settings_btn' ).on( 'click', submitSettings ); addFormNameModalEvents(); diff --git a/js/src/components/class-tabs-navigator.js b/js/src/components/tabs/class-tabs-navigator.js similarity index 78% rename from js/src/components/class-tabs-navigator.js rename to js/src/components/tabs/class-tabs-navigator.js index d32cf01bd8..1b8c201ad2 100644 --- a/js/src/components/class-tabs-navigator.js +++ b/js/src/components/tabs/class-tabs-navigator.js @@ -1,3 +1,6 @@ +import { applyContentFilter, getFilterTarget } from './filter.js'; +import { observeVisibility, disconnectVisibilityObserver } from 'core/utils/visibilityObserver'; + export class frmTabsNavigator { constructor( wrapper ) { if ( wrapper === undefined ) { @@ -17,12 +20,15 @@ export class frmTabsNavigator { this.slides = this.wrapper.querySelectorAll( '.frm-tabs-slide-track > div' ); this.isRTL = document.documentElement.dir === 'rtl' || document.body.dir === 'rtl'; this.resizeObserver = null; + this.filterTarget = getFilterTarget( this.wrapper ); this.init(); } init() { - if ( null === this.wrapper || ! this.navs.length || null === this.slideTrackLine || null === this.slideTrack || ! this.slides.length ) { + const isMissingTrackAndFilter = ! this.filterTarget && ( null === this.slideTrack || 0 === this.slides.length ); + + if ( null === this.wrapper || ! this.navs.length || null === this.slideTrackLine || isMissingTrackAndFilter ) { return; } @@ -30,11 +36,15 @@ export class frmTabsNavigator { nav.addEventListener( 'click', event => this.onNavClick( event, index ) ); if ( nav.classList.contains( 'frm-active' ) ) { this.initSlideTrackUnderline( nav ); + if ( this.filterTarget ) { + applyContentFilter( nav.dataset.filter || 'all' ); + } } } ); this.slideTrackLine.style.display = 'block'; this.setupScrollbarObserver(); + this.setupVisibilityObserver(); // Cleanup observers when page unloads to prevent memory leaks window.addEventListener( 'beforeunload', this.cleanupObservers ); } @@ -47,6 +57,12 @@ export class frmTabsNavigator { this.removeActiveClassnameFromNavs(); navItem.classList.add( 'frm-active' ); this.initSlideTrackUnderline( navItem ); + + if ( this.filterTarget ) { + applyContentFilter( navItem.dataset.filter || 'all' ); + return; + } + this.changeSlide( index ); // Handle special case for frm_insert_fields_tab @@ -62,8 +78,19 @@ export class frmTabsNavigator { } /** - * Sets up a ResizeObserver to watch for scrollbar changes in the parent container. - * Automatically repositions the underline indicator when layout changes occur. + * Automatically repositions the underline indicator when the wrapper becomes visible. + */ + setupVisibilityObserver() { + observeVisibility( this.wrapper, () => { + const activeNav = this.wrapper.querySelector( '.frm-tabs-navs ul > li.frm-active' ); + if ( activeNav ) { + this.positionUnderlineIndicator( activeNav ); + } + } ); + } + + /** + * Uses ResizeObserver to reposition the underline indicator when the parent container layout changes. */ setupScrollbarObserver() { const resizeObserverTarget = document.querySelector( '.frm-scrollbar-wrapper, .styling_settings' ) || document.body; @@ -88,6 +115,7 @@ export class frmTabsNavigator { this.resizeObserver.disconnect(); this.resizeObserver = null; } + disconnectVisibilityObserver(); } /** diff --git a/js/src/components/tabs/filter.js b/js/src/components/tabs/filter.js new file mode 100644 index 0000000000..9b01a9b11d --- /dev/null +++ b/js/src/components/tabs/filter.js @@ -0,0 +1,47 @@ +let filterTarget; + +/** + * Resolves filter target from a wrapper element's data-filter-target attribute and stores it internally. + * + * @param {Element} wrapper The wrapper element containing data-filter-target. + * @return {Element|null} The filter target element if valid, null otherwise. + */ +export function getFilterTarget( wrapper ) { + filterTarget = null; + + const selector = wrapper?.dataset?.filterTarget; + if ( selector ) { + const target = document.querySelector( selector ); + if ( hasFilterableGroups( target ) ) { + filterTarget = target; + } + } + + return filterTarget; +} + +/** + * Checks if a target element has filterable groups. + * + * @param {Element} target The container element to check. + * @return {boolean} True if target has data-group children. + */ +function hasFilterableGroups( target ) { + return target.querySelectorAll( '[data-group]' ).length > 0; +} + +/** + * Applies a filter to content groups by matching filterValue against data-group attributes. + * + * @param {string} filterValue The filter key matching data-group, or 'all'. + */ +export function applyContentFilter( filterValue ) { + if ( ! filterTarget ) { + return; + } + + filterTarget.dataset.activeFilter = filterValue; + filterTarget.querySelectorAll( '[data-group]' ).forEach( group => { + group.classList.toggle( 'frm_hidden', 'all' !== filterValue && group.dataset.group !== filterValue ); + } ); +} diff --git a/js/src/core/utils/visibilityObserver.js b/js/src/core/utils/visibilityObserver.js new file mode 100644 index 0000000000..8bafe36d9b --- /dev/null +++ b/js/src/core/utils/visibilityObserver.js @@ -0,0 +1,48 @@ +/** + * @type {IntersectionObserver|null} + */ +let observer = null; + +/** + * Checks if IntersectionObserver is supported. + * + * @return {boolean} True if supported. + */ +export function isVisibilityObserverSupported() { + return 'IntersectionObserver' in window; +} + +/** + * Observes an element and calls callback once when it becomes visible. + * + * @param {Element} element The element to observe. + * @param {Function} callback Called once when element becomes visible. + */ +export function observeVisibility( element, callback ) { + if ( ! isVisibilityObserverSupported() ) { + return; + } + + if ( ! ( element instanceof Element ) || 'function' !== typeof callback ) { + return; + } + + observer = new IntersectionObserver( entries => { + if ( entries[ 0 ].isIntersecting ) { + callback(); + disconnectVisibilityObserver(); + } + } ); + + observer.observe( element ); +} + +/** + * Disconnects the visibility observer. + */ +export function disconnectVisibilityObserver() { + if ( observer ) { + observer.disconnect(); + observer = null; + } +} diff --git a/js/src/dashboard.js b/js/src/dashboard.js index 7c35f80def..b1368f6b4d 100644 --- a/js/src/dashboard.js +++ b/js/src/dashboard.js @@ -3,7 +3,7 @@ */ import { frmAnimate } from 'core/utils'; -import { frmTabsNavigator } from './components/class-tabs-navigator'; +import { frmTabsNavigator } from './components/tabs/class-tabs-navigator'; import { frmCounter } from './components/class-counter'; class frmDashboard { constructor() { diff --git a/js/src/settings-components/components/formActionsSearch.js b/js/src/settings-components/components/formActionsSearch.js new file mode 100644 index 0000000000..8a5176382b --- /dev/null +++ b/js/src/settings-components/components/formActionsSearch.js @@ -0,0 +1,83 @@ +/** + * Form actions search behavior for the Actions & Notifications settings page. + * + * @since x.x + */ + +const ACTIONS_LIST_WRAPPER_ID = 'frm_email_addon_menu'; +const FILTER_CONTENT_ID = 'frm-actions-filter-content'; + +/** + * Initializes search behavior for the form actions settings page. + */ +export const initFormActionsSearch = () => { + const actionsListWrapper = document.getElementById( ACTIONS_LIST_WRAPPER_ID ); + if ( ! actionsListWrapper ) { + return; + } + + const searchInput = actionsListWrapper.querySelector( '.frm-auto-search' ); + if ( ! searchInput ) { + return; + } + + const filterContent = document.getElementById( FILTER_CONTENT_ID ); + const onSearch = () => handleSearchInput( searchInput, actionsListWrapper, filterContent ); + + searchInput.addEventListener( 'input', onSearch ); + searchInput.addEventListener( 'search', onSearch ); +}; + +/** + * Handles search input for form actions. + * Switches to "All" tab and defers group heading visibility update. + * + * @since x.x + * + * @param {HTMLInputElement} searchInput The search input element. + * @param {HTMLElement} actionsListWrapper The actions wrapper container. + * @param {HTMLElement|null} filterContent The filter content container. + */ +const handleSearchInput = ( searchInput, actionsListWrapper, filterContent ) => { + if ( searchInput.value.trim() ) { + switchToAllTab( actionsListWrapper ); + } + + if ( filterContent ) { + // Defer heading update so the generic searchContent handler in admin.js + // finishes toggling individual item visibility first. + setTimeout( () => updateGroupHeadingVisibility( filterContent ), 0 ); + } +}; + +/** + * Switches the active filter tab to "All" so search spans every category. + * + * @param {HTMLElement} actionsListWrapper The actions wrapper container. + */ +const switchToAllTab = actionsListWrapper => { + const allTab = actionsListWrapper.querySelector( 'li[data-filter="all"]' ); + if ( ! allTab.classList.contains( 'frm-active' ) ) { + allTab.click(); + } +}; + +/** + * Toggles group headings visibility when all their actions are hidden by search. + * + * @param {HTMLElement} filterContent The filter content container. + */ +const updateGroupHeadingVisibility = filterContent => { + filterContent.querySelectorAll( '[data-group]' ).forEach( group => { + const heading = group.querySelector( '.frm-group-heading' ); + if ( ! heading ) { + return; + } + + const actions = group.querySelectorAll( '.frm-action' ); + const allHidden = actions.length > 0 && Array.from( actions ).every( + action => action.classList.contains( 'frm_hidden' ) + ); + heading.classList.toggle( 'frm-force-hidden', allHidden ); + } ); +}; diff --git a/js/src/settings-components/components/tabs-component.js b/js/src/settings-components/components/tabs-component.js index 24a970a762..9800e3d01a 100644 --- a/js/src/settings-components/components/tabs-component.js +++ b/js/src/settings-components/components/tabs-component.js @@ -1,4 +1,4 @@ -import { frmTabsNavigator } from '../../components/class-tabs-navigator'; +import { frmTabsNavigator } from '../../components/tabs/class-tabs-navigator'; /** * Represents a Tabs Component. * diff --git a/js/src/settings-components/index.js b/js/src/settings-components/index.js index b31c543413..afd52363fe 100644 --- a/js/src/settings-components/index.js +++ b/js/src/settings-components/index.js @@ -15,6 +15,7 @@ import { initToggleGroupComponents, setupUnitInputHandlers } from './components'; +import { initFormActionsSearch } from './components/formActionsSearch'; domReady( () => { new frmRadioComponent(); @@ -24,4 +25,5 @@ domReady( () => { initTokenInputFields(); initToggleGroupComponents(); setupUnitInputHandlers(); + initFormActionsSearch(); } ); diff --git a/js/src/web-components/frm-tab-navigator-component/frm-tab-navigator-component.js b/js/src/web-components/frm-tab-navigator-component/frm-tab-navigator-component.js index 8fa0b64e29..25b59d6c93 100644 --- a/js/src/web-components/frm-tab-navigator-component/frm-tab-navigator-component.js +++ b/js/src/web-components/frm-tab-navigator-component/frm-tab-navigator-component.js @@ -1,4 +1,4 @@ -import { frmTabsNavigator } from '../../components/class-tabs-navigator'; +import { frmTabsNavigator } from '../../components/tabs/class-tabs-navigator'; import { frmWebComponent } from '../frm-web-component'; import style from './frm-tab-navigator-component.css'; diff --git a/js/src/web-components/frm-tab-navigator-component/frm-tab-navigator-component.scss b/js/src/web-components/frm-tab-navigator-component/frm-tab-navigator-component.scss index 04d9cc63ec..5745da9c53 100644 --- a/js/src/web-components/frm-tab-navigator-component/frm-tab-navigator-component.scss +++ b/js/src/web-components/frm-tab-navigator-component/frm-tab-navigator-component.scss @@ -1,4 +1,5 @@ -@import '../frm-web-component.scss'; +@import '../frm-web-component'; + .frm-tabs-wrapper { position: relative; overflow: hidden; @@ -38,6 +39,7 @@ .frm-tabs-navs { padding: 0; min-height: 44px; + ul { margin: 0; height: var(--h-md); @@ -45,7 +47,7 @@ display: flex; justify-content: space-between; list-style-type: none; - padding: 0px; + padding: 0; li, li a { @@ -62,18 +64,23 @@ margin-top: var(--gap-xs); margin-bottom: 0; cursor: pointer; + white-space: nowrap; } } } + .frm-tabs-navs ul li.frm-active, .frm-style-tabs-wrapper .frm-tabs-navs ul li.frm-active a { color: var(--grey-900); } + .frm-tabs-navs ul li:first-child { margin-left: var(--gap-xs); } + .frm-tabs-navs ul li:last-child { margin-right: var(--gap-xs); } + .frm-tabs-delimiter { position: absolute; top: 0; @@ -96,16 +103,19 @@ display: none; } } + .frm-tabs-container { position: relative; overflow: hidden; margin-top: var(--gap-md); height: 100%; } + .frm-tabs-container .frm-tabs-slide-track { display: flex; transition: 0.32s transform cubic-bezier(0.25, 0.46, 0.45, 0.94); } + .frm-tabs-slide-track > div { flex: 0 0 100%; opacity: 0; @@ -116,6 +126,7 @@ overflow: hidden; box-sizing: border-box; } + .frm-tabs-slide-track > div > div { overflow: auto; position: relative; @@ -123,9 +134,11 @@ padding: 0; box-sizing: border-box; } + .frm-tabs-slide-track > div > div:first-child { height: 100%; } + .frm-tabs-slide-track > div.frm-active { opacity: 1; transition: 0.35s opacity linear; diff --git a/resources/scss/admin/base/_variables.scss b/resources/scss/admin/base/_variables.scss index 9015004a08..6b6acc7e71 100644 --- a/resources/scss/admin/base/_variables.scss +++ b/resources/scss/admin/base/_variables.scss @@ -51,6 +51,7 @@ --success-50: #ecfdf3; --success-25: #f6fef9; --border-radius: 35px; + --x-small-radius: 4px; --small-radius: 8px; --medium-radius: 16px; --small-sidebar: 275px; diff --git a/resources/scss/admin/base/typography/_text.scss b/resources/scss/admin/base/typography/_text.scss index 4a38705963..dd0336ae59 100644 --- a/resources/scss/admin/base/typography/_text.scss +++ b/resources/scss/admin/base/typography/_text.scss @@ -23,7 +23,6 @@ body:not(.frm-admin-page-styles):not(.frm-admin-page-style) .with_frm_style .frm } a, -.widget .widget-top, .stuffbox h3, .frm-collapsed { cursor: pointer; diff --git a/resources/scss/admin/components/_page-collapsed.scss b/resources/scss/admin/components/_page-collapsed.scss index b17f67144f..f574a713f7 100644 --- a/resources/scss/admin/components/_page-collapsed.scss +++ b/resources/scss/admin/components/_page-collapsed.scss @@ -60,14 +60,6 @@ transform: rotate(-90deg); } -.open .widget-top .widget-title-action button .frmsvg { - transform: rotate(90deg); -} - -.widget-top .widget-title-action button .frmsvg use { - color: var(--grey); -} - .frm-collapsed + .frm-collapse-me { overflow: hidden !important; } diff --git a/resources/scss/admin/components/builder/_field-dragging.scss b/resources/scss/admin/components/builder/_field-dragging.scss index a9090d3cb5..c7a1145972 100644 --- a/resources/scss/admin/components/builder/_field-dragging.scss +++ b/resources/scss/admin/components/builder/_field-dragging.scss @@ -49,7 +49,6 @@ li.frm_noallow.button, opacity: 0.5; } -.frm_actions_list a.frm_show_upgrade.frm_inactive_action::before, li.frm_noallow.button.frm_show_upgrade { cursor: pointer; } diff --git a/resources/scss/admin/components/card/_card-item.scss b/resources/scss/admin/components/card/_card-item.scss index 69a35ffd5c..14917eb369 100644 --- a/resources/scss/admin/components/card/_card-item.scss +++ b/resources/scss/admin/components/card/_card-item.scss @@ -9,10 +9,27 @@ padding: var(--gap-sm); box-shadow: var(--box-shadow-xs); background: #fff; -} -.frm-card-item:not(.frm-counter-card) { - gap: 10px; + &:not(.frm-counter-card) { + gap: 10px; + } + + &.frm-card-item--outlined { + padding: var(--gap-sm) var(--gap-md); + gap: 12px; + border-color: var(--grey-300); + box-shadow: var(--box-shadow-sm); + transition: box-shadow 200ms ease-in-out; + + &:hover { + box-shadow: var(--box-shadow-md); + } + + p, + h3 { + margin: 0; + } + } } .frm-compact-card-item { diff --git a/resources/scss/admin/components/form/_fields-part3.scss b/resources/scss/admin/components/form/_fields-part3.scss index 25064fa4c9..a5975e4f6a 100644 --- a/resources/scss/admin/components/form/_fields-part3.scss +++ b/resources/scss/admin/components/form/_fields-part3.scss @@ -30,10 +30,6 @@ label input[type="radio"] { padding: 10px 15px; } -.frm_form_builder .widget-top a.widget-action::after { - margin: 7px 12px 0; -} - .frmbutton.frm_tgateway { display: none !important; } diff --git a/resources/scss/admin/components/form/_form-actions.scss b/resources/scss/admin/components/form/_form-actions.scss index 1e5e12f62a..b182c9bb52 100644 --- a/resources/scss/admin/components/form/_form-actions.scss +++ b/resources/scss/admin/components/form/_form-actions.scss @@ -1,313 +1,169 @@ -/** - * Form Actions Tab styles - */ - -.frm_email_reply_container select, -.frm_email_reply_container input, -.form-table td.frm_150_width { - width: 170px; -} +.frm_email_settings { -#frm_notification_settings .frm_no_top_padding { - padding-top: 0; -} + h2, + h3 { + border: 0; + padding: 0; + font-weight: 500 !important; + } -.frm_email_settings.frm_email_settings.widgets-holder-wrap { - overflow: auto; - box-shadow: none; -} + h3 { + font-size: var(--text-sm); + color: var(--grey-600); + margin: var(--gap-md) 0; + } -#frm_notification_settings .widget-top .widget-action, -#frm_form_editor_container .widget-top .widget-action { - border: 0; - margin: 0; - padding: 8px; - background: 0 0; - cursor: pointer; - outline: 0; -} - -#frm_notification_settings .widget-top .widget-action { - padding-top: 13.5px; -} - -#frm_email_addon_menu { - border: 1px solid var(--grey-300); - padding: var(--gap-sm); - border-radius: 4px; - margin: var(--gap-md) 0 var(--gap-sm); + .frm-border-icon .frmsvg { + color: var(--primary-700); + } } +// Search #frm_email_addon_menu .frm-search { - float: right; - margin: 0; -} - -#frm_email_addon_menu h3.frm-no-border { - clear: none; - padding-top: 7px; -} - -.frm_email_settings .widget .widget-top { - background-color: var(--sidebar-color); -} - -.frm_email_settings .widget .widget-top, -.frm_email_settings .widget .widget-top h3 { - cursor: pointer !important; -} - -.frm_email_settings .widget { - margin-bottom: var(--gap-sm); -} - -.frm_form_action_settings .widget-top { - box-shadow: none; - border-color: var(--grey-300); - border-radius: var(--small-radius); - background: var(--lightest-grey); - color: var(--grey-700); -} - -.frm_form_action_settings:hover .widget-top { - border-color: var(--grey); -} - -.frm_form_action_settings.open .widget-top { - border-bottom-left-radius: 0; - border-bottom-right-radius: 0; - border-bottom: none; -} + float: unset; + clear: unset; + width: 100%; + max-width: 380px; -.frm_form_action_settings.open:hover .widget-top { - border-color: var(--grey-300); + input[type="search"] { + height: 44px; + } } -.frm_form_action_settings > .widget-inside { +// Filter +#frm-actions-filter-content:not([data-active-filter="all"]) .frm-group-heading { display: none; } -.frm_form_action_settings.open > .widget-inside { - display: block; -} - -.frm_form_action_settings .widget-inside { - min-height: 25px; - padding: 15px; - border-color: var(--grey-300); - border-bottom-left-radius: 4px; - border-bottom-right-radius: 4px; -} - -.frm_form_action_settings .widget-title h4, -.frm_form_action_settings .widget-title h3 { - display: inline-block; - border-bottom: none; - padding: 10px 10px 5px; - font-size: var(--text-md); - font-weight: 500; -} - -#frm_email_addon_menu h3 { - margin: 0 0 var(--gap-sm); - clear: both; -} - -.frm_single_api_settings p > label { - display: inline; -} - -.frm_form_action_icon { - margin-right: 5px; -} - -.frm_actions_list { +// Action Cards +.frm_actions_list .frm-card-item.frm-card-item--outlined { + padding-left: var(--gap-sm); + padding-right: var(--gap-sm); margin: 0; - display: inline; -} - -.frm_actions_list li { - float: left; - width: 15.6%; - margin: 10px 0.5% 15px; - height: 100px; - text-align: center; -} - -.frm-limited-actions .frm-group-heading, -.frm-limited-actions #frm-hide-groups, -.frm-all-actions #frm-show-groups, -.frm-limited-actions .frm-not-installed:not(.frm-search-result):not(.frm-default-show) { - display: none; -} - -label.frm_action_events { - padding-left: 15px; -} - -#frm-hide-groups, -#frm-show-groups { - font-size: var(--text-md); - float: right; -} - -.frm_actions_list a:active, -.frm_actions_list a:focus { - outline: none; -} - -.frm_actions_list a { - font-size: var(--text-sm); - color: var(--grey-700); - word-break: break-word; -} - -.frm_actions_list span.frm-outer-circle { - - /* 50px total with 30px content */ - background-color: var(--grey-100); - padding: 10px; - text-align: center; - border-radius: 50%; - display: block; - width: 30px; - height: 30px; - margin: 0 auto 15px; - line-height: 1; -} - -.frm_email_settings .widget-title h4 { - color: var(--grey-700); -} - -.frm_disabled_action .widget-title h4 { - color: var(--grey); -} - -.frm_actions_list a .frmsvg, -.frm_actions_list a i { - height: 18px; - width: 18px; - font-size: 18px; - padding: 2px; - color: var(--lightest-grey); -} - -span.frm-inner-circle, -.frm_email_settings .widget-title .frm_form_action_icon { - background-color: var(--grey-400); - border-radius: 50%; - display: inline-block; - text-align: center; - line-height: 1; -} - -span.frm-inner-circle { - background-color: var(--primary-700); - height: 22px; - width: 22px; - padding: 4px; - color: #fff; -} - -.frm-inner-circle svg { - fill: currentColor; -} - -.frm_email_settings .widget-title .frm_form_action_icon { - height: 15px; - width: 15px; - padding: 5px; - color: #fff; - vertical-align: middle; -} - -.frm_actions_list .frmsvg, -.frm_actions_list i::before, -.frm_email_settings .widget-title .frm_form_action_icon i, -.frm_email_settings .widget-title .frm_form_action_icon .frmsvg { - height: 15px; - width: 15px; - vertical-align: text-top; -} - -.frm_actions_list i::before { - vertical-align: middle; -} - -.frm_email_settings .widget-title:hover .frm_form_action_icon { - background-color: var(--grey); -} - -span.frm-inner-circle.frm-inverse { - background-color: transparent; - color: var(--primary-700); - padding: 0; - height: 30px; - width: 100%; -} - -.frm_actions_list span.frm-inverse i, -.frm_actions_list span.frm-inverse .frmsvg { - color: var(--primary-700); - height: 30px; - width: 30px; - font-size: 30px; - padding: 0; -} - -.frm_actions_list i.frm-inverse::before { - height: 30px; - width: 100%; - font-size: 30px; -} - -.frm_email_settings .widget-title .frm_form_action_icon.frm-inverse { - background: #fff; - padding: 0; - height: 24px; - width: 24px; -} - -.frm_email_settings .widget-title .frm_form_action_icon.frm-inverse .frmsvg, -.frm_email_settings .widget-title .frm_form_action_icon.frm-inverse i::before { - color: var(--grey-400); - height: 24px; - width: 24px; - font-size: 24px; -} - -.frm_email_settings .widget-title:hover .frm_form_action_icon.frm-inverse .frmsvg, -.frm_email_settings .widget-title:hover .frm_form_action_icon.frm-inverse i::before { - color: var(--grey-500); -} - -.frm_email_icons { - padding: 10px 0 5px 6px; - font-size: 20px; -} - -.frm_email_icons a { - margin-left: 8px; - color: var(--grey); - opacity: 0; - transition: all 0.2s ease; -} - -.frm_email_icons a .frmsvg { - color: var(--grey); -} - -.frm_email_icons a:hover, -.widget-top:hover .frm_email_icons a { - opacity: 1; -} - -.frm_actions_list a.frm_inactive_action { - color: var(--grey); -} - -.frm_inactive_action .frm-inner-circle, -.frm_actions_list .frm_inactive_action i { - opacity: 0.4; + transition: box-shadow 200ms ease-in-out, background-color 200ms ease-out; + + &:hover { + background-color: var(--grey-25); + } + + h3 { + color: var(--grey-800); + + .frm-new-pill { + margin-left: 0; + } + } + + .frm_single_action { + display: none; + align-items: center; + gap: var(--gap-2xs); + font-size: var(--text-sm) !important; + padding: 7px var(--gap-sm) !important; + + .frmsvg { + width: 20px; + height: 20px; + opacity: 1; + } + + &.frm_inactive_action { + opacity: 0.55 !important; + } + } + + &:hover .frm_single_action { + display: flex; + } +} + +// Action Widgets/Settings +.frm_form_action_settings { + margin-bottom: 12px; + + h2, + h3 { + border-top: 1px solid var(--grey-200); + padding-top: var(--gap-md); + margin-bottom: var(--gap-sm); + margin-top: var(--gap-xs); + } + + .widget-top { + color: var(--grey-700); + background: #fff; + border-radius: var(--small-radius); + border-color: var(--grey-300); + box-shadow: none; + transition: background-color 200ms ease-out; + } + + .frm_email_icons { + display: none; + } + + .widget-action { + color: var(--grey-800); + transition: transform 200ms ease-out; + } + + &:hover { + + .widget-top { + background: var(--grey-25); + } + + .frm_email_icons { + display: flex; + } + } + + &.open { + + .widget-top { + background: var(--grey-25); + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; + border-bottom: none; + } + + .widget-action { + transform: rotate(-180deg); + } + } + + > .widget-inside { + display: none; + } + + &.open > .widget-inside { + display: block; + } + + .widget-inside { + min-height: 25px; + padding: var(--gap-xs) var(--gap-sm); + border-color: var(--grey-300); + border-bottom-left-radius: var(--small-radius); + border-bottom-right-radius: var(--small-radius); + + .frm_add_remove[style*="display: block"] + p { + margin-top: 0; + margin-bottom: var(--gap-xs); + } + + p { + margin-bottom: var(--gap-sm); + + > label { + display: flex; + align-items: center; + gap: var(--gap-xs); + + .frmsvg { + display: flex; + } + } + } + } } diff --git a/resources/scss/admin/components/form/_form-editor-container.scss b/resources/scss/admin/components/form/_form-editor-container.scss index 07c2f04c6b..fb18438474 100644 --- a/resources/scss/admin/components/form/_form-editor-container.scss +++ b/resources/scss/admin/components/form/_form-editor-container.scss @@ -48,8 +48,3 @@ #styling_settings input[type="radio"] { border: solid 1px #bbb; } - -#styling_settings .widget .widget-top, -#frm_form_editor_container .widget .widget-top { - cursor: pointer; -} diff --git a/resources/scss/admin/components/form/_inputs.scss b/resources/scss/admin/components/form/_inputs.scss index 7267c28c60..fc9500d49e 100644 --- a/resources/scss/admin/components/form/_inputs.scss +++ b/resources/scss/admin/components/form/_inputs.scss @@ -30,7 +30,7 @@ button.frm_choose_image_box, outline: 0; box-shadow: var(--box-shadow-xs); border-radius: var(--small-radius); - padding: 5px 14px; + padding: 5px 12px; border-color: var(--grey-300); color: var(--grey-800); font-size: var(--text-md); diff --git a/resources/scss/admin/components/icons/_circled-icons.scss b/resources/scss/admin/components/icons/_circled-icons.scss index 86f2f63a80..411feda621 100644 --- a/resources/scss/admin/components/icons/_circled-icons.scss +++ b/resources/scss/admin/components/icons/_circled-icons.scss @@ -48,6 +48,17 @@ height: 40px; border: 1px solid var(--grey-300); border-radius: var(--small-radius); + + &--small { + width: 24px; + height: 24px; + border-radius: var(--x-small-radius); + + .frmsvg { + width: 14px; + height: 14px; + } + } } .frm-upgrade-message img { diff --git a/resources/scss/admin/components/panel/_settings-components.scss b/resources/scss/admin/components/panel/_settings-components.scss index 4e0fd3570f..1390e8ba58 100644 --- a/resources/scss/admin/components/panel/_settings-components.scss +++ b/resources/scss/admin/components/panel/_settings-components.scss @@ -2,11 +2,6 @@ * Component: Settings and Panel */ -/* Form Settings Tabs */ -.frm-form-setting-tabs { - margin-top: var(--gap-sm) !important; -} - .frm-right-panel > .postbox { background-color: transparent; border: none; @@ -27,17 +22,31 @@ position: relative; } -.frm-right-panel .inside a, -.frm-form-setting-tabs a { +.frm-right-panel .inside a { font-size: var(--text-md); color: var(--grey-700); padding: var(--gap-sm); display: block; } -.frm-form-setting-tabs a { - color: var(--grey-900); - padding: var(--gap-sm) var(--gap-md); +.frm-form-setting-tabs { + margin-top: 19px !important; + + a { + font-size: var(--text-md); + color: var(--grey-900); + border-radius: var(--small-radius); + padding: 13px var(--gap-sm); + margin-left: var(--gap-xs); + margin-right: var(--gap-xs); + transition: background-color 0.2s ease-out; + + &:hover { + color: var(--grey-900); + background: var(--grey-100); + } + } + } .frm-right-panel .inside a { @@ -50,14 +59,7 @@ color: var(--primary-700); } -.frm-form-setting-tabs a:hover { - background: var(--sidebar-hover); - color: var(--grey-900); -} - -.frm-right-panel .inside i, -.frm-form-setting-tabs a i, -.frm-form-setting-tabs a .frmsvg { +.frm-right-panel .inside i { margin: 0 5px; display: inline-block; width: 20px; @@ -65,14 +67,8 @@ color: var(--grey-500); } -.frm-form-setting-tabs a .frmsvg { - margin: 0; -} - .frm-right-panel .inside a:hover i, -.frm-right-panel .inside a:hover .frmsvg, -.frm-form-setting-tabs a:hover .frmsvg, -.frm-form-setting-tabs a:hover span { +.frm-right-panel .inside a:hover .frmsvg { color: var(--grey-700); } @@ -101,9 +97,9 @@ input.frm_enternew { .frm_posttax_opt_list { border: 1px solid var(--grey-300); - padding: var(--gap-md); + padding: var(--gap-md) var(--gap-sm); border-radius: var(--small-radius); - margin: 5px 0 5px var(--gap-md); + margin: var(--gap-sm) 0; } /* Color picker CSS */ diff --git a/resources/scss/admin/components/settings/_adv-info.scss b/resources/scss/admin/components/settings/_adv-info.scss index d2b8c3a8ed..b9ad2df8f0 100644 --- a/resources/scss/admin/components/settings/_adv-info.scss +++ b/resources/scss/admin/components/settings/_adv-info.scss @@ -47,12 +47,6 @@ font-weight: 600; } -#form_settings_page .frm-inner-content { - padding-top: 0; - padding-bottom: 150px; - position: relative; -} - .frm_wrap #submitdiv { margin-bottom: 0; border-width: 0 0 1px; diff --git a/resources/scss/admin/components/settings/_form-settings.scss b/resources/scss/admin/components/settings/_form-settings.scss index 342d173403..3b0132e5e4 100644 --- a/resources/scss/admin/components/settings/_form-settings.scss +++ b/resources/scss/admin/components/settings/_form-settings.scss @@ -2,6 +2,13 @@ * Form Settings Tab styles */ +#form_settings_page .frm-inner-content { + position: relative; + padding-right: var(--gap-xl); + padding-bottom: 150px; + padding-left: var(--gap-xl); +} + h2.frm-h2, .frm_form_settings h2 { border-bottom: 1px solid var(--grey-300); @@ -15,7 +22,7 @@ h2.frm-h2, .frm_form_settings h3, .frm_form_settings span.frm_add_logic_link { font-size: var(--text-md); - border-top: 1px solid var(--grey-300); + border-top: 1px solid var(--grey-200); padding-top: var(--gap-sm); margin: var(--gap-sm) 0; font-weight: 400; @@ -42,6 +49,11 @@ h2.frm-h2, width: 95%; } +.frm_form_settings .frm-form-setting-tabs a .frmsvg { + width: 24px; + height: 24px; +} + .frm_wrap .ui-autocomplete { padding: 3px 0; max-height: 310px; diff --git a/resources/scss/admin/components/settings/_tabs.scss b/resources/scss/admin/components/settings/_tabs.scss index 829150f7f8..d0419bd633 100644 --- a/resources/scss/admin/components/settings/_tabs.scss +++ b/resources/scss/admin/components/settings/_tabs.scss @@ -66,7 +66,7 @@ margin-top: var(--gap-xs); margin-bottom: 0; cursor: pointer; - + white-space: nowrap; &.frm-active, &.frm-active a { diff --git a/resources/scss/admin/media-queries/_screen-desktop.scss b/resources/scss/admin/media-queries/_screen-desktop.scss index 118d431cdd..c4d42838c1 100644 --- a/resources/scss/admin/media-queries/_screen-desktop.scss +++ b/resources/scss/admin/media-queries/_screen-desktop.scss @@ -7,6 +7,18 @@ #frm_top_bar h1 { min-width: 0; /* Reset the min-width to prevent menu items from stacking vertically */ } + + #frm_email_addon_menu { + > :first-child.frm-h-stack { + flex-direction: column; + align-items: stretch; + gap: var(--gap-sm); + } + + .frm-search { + max-width: unset; + } + } } @media only screen and (max-width: 1050px) { @@ -20,10 +32,6 @@ margin: 0; } - .frm_actions_list { - margin-left: 0; - } - #frm_bs_dropdown .frm_bstooltip { /* There isn't enough room for the title on a screen this size so just hide it. */ diff --git a/resources/scss/admin/utilities/Interactivity/_cursor.scss b/resources/scss/admin/utilities/Interactivity/_cursor.scss index 56ca49dd50..dbd2ae2221 100644 --- a/resources/scss/admin/utilities/Interactivity/_cursor.scss +++ b/resources/scss/admin/utilities/Interactivity/_cursor.scss @@ -3,5 +3,5 @@ */ .frm-cursor-pointer { - cursor: pointer; + cursor: pointer !important; } diff --git a/resources/scss/admin/utilities/border/_border.scss b/resources/scss/admin/utilities/border/_border.scss index c8aacf7e07..1f76c4f1d6 100644 --- a/resources/scss/admin/utilities/border/_border.scss +++ b/resources/scss/admin/utilities/border/_border.scss @@ -9,3 +9,7 @@ .frm-no-border { border: none !important; } + +.frm-bt-200 { + border-top: 1px solid var(--grey-200); +} diff --git a/resources/scss/admin/utilities/spacing/_margin.scss b/resources/scss/admin/utilities/spacing/_margin.scss index 5051d5b52e..921f4bf1a6 100644 --- a/resources/scss/admin/utilities/spacing/_margin.scss +++ b/resources/scss/admin/utilities/spacing/_margin.scss @@ -97,6 +97,10 @@ margin-left: auto; } +.frm-ml-auto-force { + margin-left: auto !important; +} + .-frm-ml-2xs { margin-left: calc(-1 * var(--gap-2xs)) !important; } diff --git a/resources/scss/admin/utilities/spacing/_padding.scss b/resources/scss/admin/utilities/spacing/_padding.scss index 403ecdfb09..4d282078ee 100644 --- a/resources/scss/admin/utilities/spacing/_padding.scss +++ b/resources/scss/admin/utilities/spacing/_padding.scss @@ -11,6 +11,10 @@ padding: var(--gap-2xs); } +.frm-p-2xs-force { + padding: var(--gap-2xs) !important; +} + .frm-p-sm, .frm-p-4 { padding: var(--gap-sm) !important; @@ -115,3 +119,8 @@ padding-right: var(--gap-md) !important; padding-left: var(--gap-md) !important; } + +.frm-children-px-sm > * { + padding-right: var(--gap-sm) !important; + padding-left: var(--gap-sm) !important; +} diff --git a/resources/scss/admin/utilities/typography/_text-color.scss b/resources/scss/admin/utilities/typography/_text-color.scss index 8079d9ea71..38e78cf778 100644 --- a/resources/scss/admin/utilities/typography/_text-color.scss +++ b/resources/scss/admin/utilities/typography/_text-color.scss @@ -27,7 +27,7 @@ } .frm-text-grey-800 { - color: var(--grey-800); + color: var(--grey-800) !important; } .frm-text-grey-900 { @@ -41,3 +41,7 @@ .frm-text-warning-500 { color: var(--warning-500); } + +.frm-text-primary-700 { + color: var(--primary-700); +} diff --git a/resources/scss/admin/utilities/typography/_text-transform.scss b/resources/scss/admin/utilities/typography/_text-transform.scss index 894e1107b5..2c1093993c 100644 --- a/resources/scss/admin/utilities/typography/_text-transform.scss +++ b/resources/scss/admin/utilities/typography/_text-transform.scss @@ -3,5 +3,5 @@ */ .frm-capitalize { - text-transform: capitalize; + text-transform: capitalize !important; } diff --git a/stripe/models/FrmTransLiteAction.php b/stripe/models/FrmTransLiteAction.php index 5b9d4e868e..95b4f31c4b 100755 --- a/stripe/models/FrmTransLiteAction.php +++ b/stripe/models/FrmTransLiteAction.php @@ -14,7 +14,7 @@ public function __construct() { // After user registration. 'priority' => 45, 'event' => array( 'create' ), - 'color' => 'var(--green)', + 'color' => '#3fac25', ); $this->FrmFormAction( 'payment', __( 'Collect a Payment', 'formidable' ), $action_ops ); diff --git a/webpack.dev.js b/webpack.dev.js index c55056ce94..7362735834 100755 --- a/webpack.dev.js +++ b/webpack.dev.js @@ -2,12 +2,12 @@ * Formidable Forms Development Server * * Run `npm run serve` to start the development server. You'll be prompted - * for your WordPress site URL, and a development server will start at localhost:3000. + * for your WordPress site URL and JS/PHP watching, then a dev server starts at localhost:8880. * * Development is streamlined with automatic updates: * - CSS/SCSS changes are injected in real-time without page refresh - * - JavaScript and PHP changes trigger an automatic full page reload - * - Browser opens automatically to localhost:3000 + * - JavaScript and PHP changes trigger an automatic full page reload (opt-in) + * - Browser opens automatically to localhost:8880 */ /** @@ -22,11 +22,11 @@ const webpackDevMiddleware = require( 'webpack-dev-middleware' ); */ const config = { // WordPress site URL from environment variable or default - siteDomain: process.env.SITE_URL || 'formidable.local', + siteDomain: process.env.SITE_DOMAIN || 'formidable.local', - // Server ports - port: 3000, - uiPort: 3001, + // Server ports (8880/8881 to avoid conflicts with common dev tools) + port: 8880, + uiPort: 8881, // Paths to watch for changes cssPath: '../formidable*/**/*.css', @@ -70,6 +70,11 @@ const init = () => { ] }, + // Exclude 3rd-party directories from all file watchers + watchOptions: { + ignored: [ '**/node_modules/**', '**/vendor/**' ] + }, + // File watching files: [ // CSS changes, inject without reload @@ -80,13 +85,28 @@ const init = () => { browserSyncInstance.reload( '*.css' ); } }, - // JS source changes + // JS source changes log rebuild progress (webpack handles actual compilation) { match: [ config.jsSrcPath ], fn: ( event, file ) => console.log( `JS source updated: ${ file }\nRebuilding JS bundles...` ) }, - // Conditionally watch compiled JS and PHP files - ...( process.env.WATCH_FILES && process.env.WATCH_FILES.toLowerCase().startsWith( 'y' ) ? [ config.jsPath, config.phpPath ] : [] ) + // Conditionally watch compiled JS and PHP files for full page reload + ...( process.env.ENABLE_WATCH && process.env.ENABLE_WATCH.toLowerCase().startsWith( 'y' ) ? [ + { + match: [ config.jsPath ], + fn: ( event, file ) => { + console.log( `JS updated: ${ file }` ); + browserSyncInstance.reload(); + } + }, + { + match: [ config.phpPath ], + fn: ( event, file ) => { + console.log( `PHP updated: ${ file }` ); + browserSyncInstance.reload(); + } + } + ] : [] ) ], // Server settings @@ -105,7 +125,7 @@ const init = () => { } ] } ); - console.log( `Development server running at: http://localhost:${ config.port }` ); + console.log( `Development server running at http://localhost:${ config.port }` ); }; // Start the server