+ ?>
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',
+);
?>
-
+
+
>
'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