Snapshot: MLS sync fixes, image refresh, plugin/theme updates

MLS plugin fixes from this session:
- Fix silent insert failures: location column NOT NULL was rejecting wpdb->insert calls,
  causing ~18k new properties since Dec 2025 to be lost. Inserts now build raw SQL
  with ST_PointFromText so the spatial column is populated atomically.
- Auto-refresh expired media URLs in MLS_Media_Handler::fetch_and_cache(), guarded by
  a property-level GET_LOCK so concurrent fetches share one API refresh.
- Normalize WP_Error to null in mls_get_property_image() so callers can rely on the
  documented string|null contract.
- Support comma-separated property_type filters in MLS_Query and MLS_Cluster so the
  homepage "View All Commercial" link (?property_type=Commercial+Sale,Land,Farm)
  actually filters correctly.
- Incremental sync now looks back 10 minutes past the latest modification timestamp
  as a safety margin against missed records.
- Smart sync exits silently (info-level, not warning) when a full sync is in progress.

Operational:
- New cron: weekly full sync Sundays at 3 AM (/usr/local/bin/mls-full-sync).
- New cron: hourly 2GB cap on mls-thumbnails/ and cache/transformed-images/
  (/usr/local/bin/mls-image-cache-cap).
- Logrotate config for wp-content/debug.log (2-day retention, daily rotation,
  delaycompress).

Repo policy:
- CLAUDE.md updated with explicit "commit everything except build artifacts" policy.
- .gitignore: untrack runtime image caches and debug.log rotations.

Other modifications in this snapshot are pre-existing in-flight theme/plugin/db_content_updates work.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
root
2026-04-29 15:32:23 +00:00
parent 57b752f54e
commit b6df4dbb92
5385 changed files with 838580 additions and 2416 deletions
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,385 @@
/* global wpforms_gutenberg_form_selector, JSX */
/* jshint es3: false, esversion: 6 */
/**
* @param strings.update_wp_notice_head
* @param strings.update_wp_notice_text
* @param strings.update_wp_notice_link
* @param strings.wpforms_empty_help
* @param strings.wpforms_empty_info
*/
const { serverSideRender: ServerSideRender = wp.components.ServerSideRender } = wp;
const { createElement, Fragment } = wp.element;
const { registerBlockType } = wp.blocks;
const { InspectorControls } = wp.blockEditor || wp.editor;
const { SelectControl, ToggleControl, PanelBody, Placeholder } = wp.components;
const { __ } = wp.i18n;
const wpformsIcon = createElement( 'svg', { width: 20, height: 20, viewBox: '0 0 612 612', className: 'dashicon' },
createElement( 'path', {
fill: 'currentColor',
d: 'M544,0H68C30.445,0,0,30.445,0,68v476c0,37.556,30.445,68,68,68h476c37.556,0,68-30.444,68-68V68 C612,30.445,581.556,0,544,0z M464.44,68L387.6,120.02L323.34,68H464.44z M288.66,68l-64.26,52.02L147.56,68H288.66z M544,544H68 V68h22.1l136,92.14l79.9-64.6l79.56,64.6l136-92.14H544V544z M114.24,263.16h95.88v-48.28h-95.88V263.16z M114.24,360.4h95.88 v-48.62h-95.88V360.4z M242.76,360.4h255v-48.62h-255V360.4L242.76,360.4z M242.76,263.16h255v-48.28h-255V263.16L242.76,263.16z M368.22,457.3h129.54V408H368.22V457.3z',
} )
);
/**
* Popup container.
*
* @since 1.8.3
*
* @type {Object}
*/
let $popup = {};
/**
* Close button (inside the form builder) click event.
*
* @since 1.8.3
*
* @param {string} clientID Block Client ID.
*/
const builderCloseButtonEvent = function( clientID ) {
$popup
.off( 'wpformsBuilderInPopupClose' )
.on( 'wpformsBuilderInPopupClose', function( e, action, formId, formTitle ) {
if ( action !== 'saved' || ! formId ) {
return;
}
// Insert a new block when a new form is created from the popup to update the form list and attributes.
const newBlock = wp.blocks.createBlock( 'wpforms/form-selector', {
formId: formId.toString(), // Expects string value, make sure we insert string.
} );
// eslint-disable-next-line camelcase
wpforms_gutenberg_form_selector.forms = [ { ID: formId, post_title: formTitle } ];
// Insert a new block.
wp.data.dispatch( 'core/block-editor' ).removeBlock( clientID );
wp.data.dispatch( 'core/block-editor' ).insertBlocks( newBlock );
} );
};
/**
* Init Modern style Dropdown fields (<select>) with choiceJS.
*
* @since 1.9.0
*
* @param {Object} e Block Details.
*/
const loadChoiceJS = function( e ) {
if ( typeof window.Choices !== 'function' ) {
return;
}
const $form = jQuery( e.detail.block.querySelector( `#wpforms-${ e.detail.formId }` ) );
const config = window.wpforms_choicesjs_config || {};
$form.find( '.choicesjs-select' ).each( function( index, element ) {
if ( ! ( element instanceof HTMLSelectElement ) ) {
return;
}
const $el = jQuery( element );
if ( $el.data( 'choicesjs' ) ) {
return;
}
const $field = $el.closest( '.wpforms-field' );
config.callbackOnInit = function() {
const self = this,
$element = jQuery( self.passedElement.element ),
$input = jQuery( self.input.element ),
sizeClass = $element.data( 'size-class' );
// Add CSS-class for size.
if ( sizeClass ) {
jQuery( self.containerOuter.element ).addClass( sizeClass );
}
/**
* If a multiple select has selected choices - hide a placeholder text.
* In case if select is empty - we return placeholder text.
*/
if ( $element.prop( 'multiple' ) ) {
// On init event.
$input.data( 'placeholder', $input.attr( 'placeholder' ) );
if ( self.getValue( true ).length ) {
$input.removeAttr( 'placeholder' );
}
}
this.disable();
$field.find( '.is-disabled' ).removeClass( 'is-disabled' );
};
$el.data( 'choicesjs', new window.Choices( element, config ) );
// Placeholder fix on iframes.
if ( $el.val() ) {
$el.parent().find( '.choices__input' ).attr( 'style', 'display: none !important' );
}
} );
};
// on document ready
jQuery( function() {
jQuery( window ).on( 'wpformsFormSelectorFormLoaded', loadChoiceJS );
} );
/**
* Open builder popup.
*
* @since 1.6.2
*
* @param {string} clientID Block Client ID.
*/
const openBuilderPopup = function( clientID ) {
if ( jQuery.isEmptyObject( $popup ) ) {
const tmpl = jQuery( '#wpforms-gutenberg-popup' );
const parent = jQuery( '#wpwrap' );
parent.after( tmpl );
$popup = parent.siblings( '#wpforms-gutenberg-popup' );
}
const url = wpforms_gutenberg_form_selector.get_started_url,
$iframe = $popup.find( 'iframe' );
builderCloseButtonEvent( clientID );
$iframe.attr( 'src', url );
$popup.fadeIn();
};
const hasForms = function() {
return wpforms_gutenberg_form_selector.forms.length > 0;
};
registerBlockType( 'wpforms/form-selector', {
title: wpforms_gutenberg_form_selector.strings.title,
description: wpforms_gutenberg_form_selector.strings.description,
icon: wpformsIcon,
keywords: wpforms_gutenberg_form_selector.strings.form_keywords,
category: 'widgets',
attributes: {
formId: {
type: 'string',
},
displayTitle: {
type: 'boolean',
},
displayDesc: {
type: 'boolean',
},
preview: {
type: 'boolean',
},
},
example: {
attributes: {
preview: true,
},
},
supports: {
customClassName: hasForms(),
},
edit( props ) { // eslint-disable-line max-lines-per-function
const { attributes: { formId = '', displayTitle = false, displayDesc = false, preview = false }, setAttributes } = props;
const formOptions = wpforms_gutenberg_form_selector.forms.map( ( value ) => (
{ value: value.ID, label: value.post_title }
) );
const strings = wpforms_gutenberg_form_selector.strings;
let jsx;
formOptions.unshift( { value: '', label: wpforms_gutenberg_form_selector.strings.form_select } );
function selectForm( value ) { // eslint-disable-line jsdoc/require-jsdoc
setAttributes( { formId: value } );
}
function toggleDisplayTitle( value ) { // eslint-disable-line jsdoc/require-jsdoc
setAttributes( { displayTitle: value } );
}
function toggleDisplayDesc( value ) { // eslint-disable-line jsdoc/require-jsdoc
setAttributes( { displayDesc: value } );
}
/**
* Get block empty JSX code.
*
* @since 1.8.3
*
* @param {Object} blockProps Block properties.
*
* @return {JSX.Element} Block empty JSX code.
*/
function getEmptyFormsPreview( blockProps ) {
const clientId = blockProps.clientId;
return (
<Fragment
key="wpforms-gutenberg-form-selector-fragment-block-empty">
<div className="wpforms-no-form-preview">
<img src={ wpforms_gutenberg_form_selector.block_empty_url } alt="" />
<p dangerouslySetInnerHTML={ { __html: strings.wpforms_empty_info } }></p>
<button type="button" className="get-started-button components-button is-button is-primary"
onClick={
() => {
openBuilderPopup( clientId );
}
}
>
{ __( 'Get Started', 'wpforms-lite' ) }
</button>
<p className="empty-desc" dangerouslySetInnerHTML={ { __html: strings.wpforms_empty_help } }></p>
{ /* Template for popup with builder iframe */ }
<div id="wpforms-gutenberg-popup" className="wpforms-builder-popup">
<iframe src="about:blank" width="100%" height="100%" id="wpforms-builder-iframe" title="wpforms-gutenberg-popup"></iframe>
</div>
</div>
</Fragment>
);
}
/**
* Print empty forms notice.
*
* @since 1.8.3
*
* @param {string} clientId Block client ID.
*
* @return {JSX.Element} Field styles JSX code.
*/
function printEmptyFormsNotice( clientId ) {
return (
<InspectorControls key="wpforms-gutenberg-form-selector-inspector-main-settings">
<PanelBody className="wpforms-gutenberg-panel" title={ strings.form_settings }>
<p className="wpforms-gutenberg-panel-notice wpforms-warning wpforms-empty-form-notice" style={ { display: 'block' } }>
<strong>{ __( 'You havent created a form, yet!', 'wpforms-lite' ) }</strong>
{ __( 'What are you waiting for?', 'wpforms-lite' ) }
</p>
<button type="button" className="get-started-button components-button is-button is-secondary"
onClick={
() => {
openBuilderPopup( clientId );
}
}
>
{ __( 'Get Started', 'wpforms-lite' ) }
</button>
</PanelBody>
</InspectorControls>
);
}
/**
* Get styling panels preview.
*
* @since 1.8.8
*
* @return {JSX.Element} JSX code.
*/
function getStylingPanelsPreview() {
return (
<Fragment>
<PanelBody className="wpforms-gutenberg-panel disabled_panel" title={ strings.themes }>
<div className="wpforms-panel-preview wpforms-panel-preview-themes"></div>
</PanelBody>
<PanelBody className="wpforms-gutenberg-panel disabled_panel" title={ strings.field_styles }>
<div className="wpforms-panel-preview wpforms-panel-preview-field"></div>
</PanelBody>
<PanelBody className="wpforms-gutenberg-panel disabled_panel" title={ strings.label_styles }>
<div className="wpforms-panel-preview wpforms-panel-preview-label"></div>
</PanelBody>
<PanelBody className="wpforms-gutenberg-panel disabled_panel" title={ strings.button_styles }>
<div className="wpforms-panel-preview wpforms-panel-preview-button"></div>
</PanelBody>
<PanelBody className="wpforms-gutenberg-panel disabled_panel" title={ strings.container_styles }>
<div className="wpforms-panel-preview wpforms-panel-preview-container"></div>
</PanelBody>
<PanelBody className="wpforms-gutenberg-panel disabled_panel" title={ strings.background_styles }>
<div className="wpforms-panel-preview wpforms-panel-preview-background"></div>
</PanelBody>
</Fragment>
);
}
if ( ! hasForms() ) {
jsx = [ printEmptyFormsNotice( props.clientId ) ];
jsx.push( getEmptyFormsPreview( props ) );
return jsx;
}
jsx = [
<InspectorControls key="wpforms-gutenberg-form-selector-inspector-controls">
<PanelBody title={ wpforms_gutenberg_form_selector.strings.form_settings }>
<SelectControl
label={ wpforms_gutenberg_form_selector.strings.form_selected }
value={ formId }
options={ formOptions }
onChange={ selectForm }
/>
<ToggleControl
label={ wpforms_gutenberg_form_selector.strings.show_title }
checked={ displayTitle }
onChange={ toggleDisplayTitle }
/>
<ToggleControl
label={ wpforms_gutenberg_form_selector.strings.show_description }
checked={ displayDesc }
onChange={ toggleDisplayDesc }
/>
<p className="wpforms-gutenberg-panel-notice wpforms-warning">
<strong>{ strings.update_wp_notice_head }</strong>
{ strings.update_wp_notice_text } <a href={ strings.update_wp_notice_link } rel="noreferrer" target="_blank">{ strings.learn_more }</a>
</p>
</PanelBody>
{ getStylingPanelsPreview() }
</InspectorControls>,
];
if ( formId ) {
jsx.push(
<ServerSideRender
key="wpforms-gutenberg-form-selector-server-side-renderer"
block="wpforms/form-selector"
attributes={ props.attributes }
/>
);
} else if ( preview ) {
jsx.push(
<Fragment
key="wpforms-gutenberg-form-selector-fragment-block-preview">
<img src={ wpforms_gutenberg_form_selector.block_preview_url } style={ { width: '100%' } } alt="" />
</Fragment>
);
} else {
jsx.push(
<Placeholder
key="wpforms-gutenberg-form-selector-wrap"
className="wpforms-gutenberg-form-selector-wrap">
<img src={ wpforms_gutenberg_form_selector.logo_url } alt="" />
<SelectControl
key="wpforms-gutenberg-form-selector-select-control"
value={ formId }
options={ formOptions }
onChange={ selectForm }
/>
</Placeholder>
);
}
return jsx;
},
save() {
return null;
},
} );
@@ -0,0 +1,164 @@
/* global wpforms_gutenberg_form_selector */
/* jshint es3: false, esversion: 6 */
/**
* @param strings.custom_css
* @param strings.custom_css_notice
* @param strings.copy_paste_settings
* @param strings.copy_paste_notice
*/
/**
* Gutenberg editor block.
*
* Advanced Settings module.
*
* @since 1.8.8
*/
export default ( function( $ ) {
/**
* WP core components.
*
* @since 1.8.8
*/
const { addFilter } = wp.hooks;
const { createHigherOrderComponent } = wp.compose;
const { Fragment } = wp.element;
const { InspectorAdvancedControls } = wp.blockEditor || wp.editor;
const { TextareaControl } = wp.components;
/**
* Localized data aliases.
*
* @since 1.8.8
*/
const { strings } = wpforms_gutenberg_form_selector;
/**
* Public functions and properties.
*
* @since 1.8.8
*
* @type {Object}
*/
const app = {
/**
* Initialize module.
*
* @since 1.8.8
*
* @param {Object} commonModule Common module.
*/
init( commonModule ) {
app.common = commonModule;
app.hooks();
app.events();
},
/**
* Hooks.
*
* @since 1.8.8
*/
hooks() {
addFilter(
'editor.BlockEdit',
'editorskit/custom-advanced-control',
app.withAdvancedControls
);
},
/**
* Events.
*
* @since 1.8.8
*/
events() {
$( document )
.on( 'focus click', 'textarea', app.copyPasteFocus );
},
/**
* Copy / Paste Style Settings textarea focus event.
*
* @since 1.8.8
*/
copyPasteFocus() {
const $input = $( this );
if ( $input.siblings( 'label' ).text() === strings.copy_paste_settings ) {
// Select all text, so it's easier to copy and paste value.
$input.select();
}
},
/**
* Get fields.
*
* @since 1.8.8
*
* @param {Object} props Block properties.
*
* @return {Object} Inspector advanced controls JSX code.
*/
getFields( props ) {
// Proceed only for WPForms block and when form ID is set.
if ( props?.name !== 'wpforms/form-selector' || ! props?.attributes?.formId ) {
return null;
}
// Common event handlers.
const handlers = app.common.getSettingsFieldsHandlers( props );
return (
<InspectorAdvancedControls>
<div className={ app.common.getPanelClass( props ) + ' advanced' }>
<TextareaControl
className="wpforms-gutenberg-form-selector-custom-css"
label={ strings.custom_css }
rows="5"
spellCheck="false"
value={ props.attributes.customCss }
onChange={ ( value ) => handlers.attrChange( 'customCss', value ) }
/>
<div className="wpforms-gutenberg-form-selector-legend" dangerouslySetInnerHTML={ { __html: strings.custom_css_notice } }></div>
<TextareaControl
className="wpforms-gutenberg-form-selector-copy-paste-settings"
label={ strings.copy_paste_settings }
rows="4"
spellCheck="false"
value={ props.attributes.copyPasteJsonValue }
onChange={ ( value ) => handlers.pasteSettings( value ) }
/>
<div className="wpforms-gutenberg-form-selector-legend" dangerouslySetInnerHTML={ { __html: strings.copy_paste_notice } }></div>
</div>
</InspectorAdvancedControls>
);
},
/**
* Add controls on Advanced Settings Panel.
*
* @param {Function} BlockEdit Block edit component.
*
* @return {Function} BlockEdit Modified block edit component.
*/
withAdvancedControls: createHigherOrderComponent(
( BlockEdit ) => {
return ( props ) => {
return (
<Fragment>
<BlockEdit { ...props } />
{ app.getFields( props ) }
</Fragment>
);
};
},
'withAdvancedControls'
),
};
// Provide access to public functions/properties.
return app;
}( jQuery ) );
@@ -0,0 +1,66 @@
/* global wpforms_gutenberg_form_selector */
/* jshint es3: false, esversion: 6 */
import PropTypes from 'prop-types';
/**
* @param strings.remove_image
*/
/**
* React component for the background preview.
*
* @since 1.8.8
*
* @param {Object} props Component props.
* @param {Object} props.attributes Block attributes.
* @param {Function} props.onRemoveBackground Function to remove the background.
* @param {Function} props.onPreviewClicked Function to handle the preview click.
*
* @return {Object} React component.
*/
const BackgroundPreview = ( { attributes, onRemoveBackground, onPreviewClicked } ) => {
const { Button } = wp.components;
const { strings } = wpforms_gutenberg_form_selector;
return (
<div className="wpforms-gutenberg-form-selector-background-preview">
<style>
{ `
.wpforms-gutenberg-form-selector-background-preview-image {
--wpforms-background-url: ${ attributes.backgroundUrl };
}
` }
</style>
<input
className="wpforms-gutenberg-form-selector-background-preview-image"
onClick={ onPreviewClicked }
tabIndex={ 0 }
type="button"
onKeyDown={
( event ) => {
if ( event.key === 'Enter' || event.key === ' ' ) {
onPreviewClicked();
}
}
}
>
</input>
<Button
isSecondary
className="is-destructive"
onClick={ onRemoveBackground }
>
{ strings.remove_image }
</Button>
</div>
);
};
BackgroundPreview.propTypes = {
attributes: PropTypes.object.isRequired,
onRemoveBackground: PropTypes.func.isRequired,
onPreviewClicked: PropTypes.func.isRequired,
};
export default BackgroundPreview;
@@ -0,0 +1,607 @@
/* global wpforms_gutenberg_form_selector */
/* jshint es3: false, esversion: 6 */
import BackgroundPreview from './background-preview.js';
/**
* @param strings.background_styles
* @param strings.bottom_center
* @param strings.bottom_left
* @param strings.bottom_right
* @param strings.center_center
* @param strings.center_left
* @param strings.center_right
* @param strings.choose_image
* @param strings.image_url
* @param strings.media_library
* @param strings.no_repeat
* @param strings.repeat_x
* @param strings.repeat_y
* @param strings.select_background_image
* @param strings.select_image
* @param strings.stock_photo
* @param strings.tile
* @param strings.top_center
* @param strings.top_left
* @param strings.top_right
*/
/**
* Gutenberg editor block.
*
* Background styles panel module.
*
* @since 1.8.8
*/
export default ( function() {
/**
* WP core components.
*
* @since 1.8.8
*/
const { PanelColorSettings } = wp.blockEditor || wp.editor;
const { SelectControl, PanelBody, Flex, FlexBlock, __experimentalUnitControl, TextControl, Button } = wp.components;
/**
* Localized data aliases.
*
* @since 1.8.8
*/
const { strings, defaults } = wpforms_gutenberg_form_selector;
/**
* Public functions and properties.
*
* @since 1.8.8
*
* @type {Object}
*/
const app = {
/**
* Get block attributes.
*
* @since 1.8.8
*
* @return {Object} Block attributes.
*/
getBlockAttributes() {
return {
backgroundImage: {
type: 'string',
default: defaults.backgroundImage,
},
backgroundPosition: {
type: 'string',
default: defaults.backgroundPosition,
},
backgroundRepeat: {
type: 'string',
default: defaults.backgroundRepeat,
},
backgroundSizeMode: {
type: 'string',
default: defaults.backgroundSizeMode,
},
backgroundSize: {
type: 'string',
default: defaults.backgroundSize,
},
backgroundWidth: {
type: 'string',
default: defaults.backgroundWidth,
},
backgroundHeight: {
type: 'string',
default: defaults.backgroundHeight,
},
backgroundColor: {
type: 'string',
default: defaults.backgroundColor,
},
backgroundUrl: {
type: 'string',
default: defaults.backgroundUrl,
},
};
},
/**
* Get Background Styles panel JSX code.
*
* @since 1.8.8
*
* @param {Object} props Block properties.
* @param {Object} handlers Block handlers.
* @param {Object} formSelectorCommon Block properties.
* @param {Object} stockPhotos Stock Photos module.
* @param {Object} uiState UI state.
*
* @return {Object} Field styles JSX code.
*/
getBackgroundStyles( props, handlers, formSelectorCommon, stockPhotos, uiState ) { // eslint-disable-line max-lines-per-function, complexity
const isNotDisabled = uiState.isNotDisabled;
const isProEnabled = uiState.isProEnabled;
const showBackgroundPreview = uiState.showBackgroundPreview;
const setShowBackgroundPreview = uiState.setShowBackgroundPreview;
const lastBgImage = uiState.lastBgImage;
const setLastBgImage = uiState.setLastBgImage;
const tabIndex = isNotDisabled ? 0 : -1;
const cssClass = formSelectorCommon.getPanelClass( props ) + ( isNotDisabled ? '' : ' wpforms-gutenberg-panel-disabled' );
return (
<PanelBody className={ cssClass } title={ strings.background_styles }>
<div // eslint-disable-line jsx-a11y/no-static-element-interactions
className="wpforms-gutenberg-form-selector-panel-body"
onClick={ ( event ) => {
if ( isNotDisabled ) {
return;
}
event.stopPropagation();
if ( ! isProEnabled ) {
return formSelectorCommon.education.showProModal( 'background', strings.background_styles );
}
formSelectorCommon.education.showLicenseModal( 'background', strings.background_styles, 'background-styles' );
} }
onKeyDown={ ( event ) => {
if ( isNotDisabled ) {
return;
}
event.stopPropagation();
if ( ! isProEnabled ) {
return formSelectorCommon.education.showProModal( 'background', strings.background_styles );
}
formSelectorCommon.education.showLicenseModal( 'background', strings.background_styles, 'background-styles' );
} }
>
<Flex gap={ 4 } align="flex-start" className={ 'wpforms-gutenberg-form-selector-flex' } justify="space-between">
<FlexBlock>
<SelectControl
label={ strings.image }
tabIndex={ tabIndex }
value={ props.attributes.backgroundImage }
options={ [
{ label: strings.none, value: 'none' },
{ label: strings.media_library, value: 'library' },
{ label: strings.stock_photo, value: 'stock' },
] }
onChange={ ( value ) => app.setContainerBackgroundImageWrapper( props, handlers, value, lastBgImage, setLastBgImage ) }
/>
</FlexBlock>
<FlexBlock>
{ ( props.attributes.backgroundImage !== 'none' || ! isNotDisabled ) && (
<SelectControl
label={ strings.position }
value={ props.attributes.backgroundPosition }
tabIndex={ tabIndex }
options={ [
{ label: strings.top_left, value: 'top left' },
{ label: strings.top_center, value: 'top center' },
{ label: strings.top_right, value: 'top right' },
{ label: strings.center_left, value: 'center left' },
{ label: strings.center_center, value: 'center center' },
{ label: strings.center_right, value: 'center right' },
{ label: strings.bottom_left, value: 'bottom left' },
{ label: strings.bottom_center, value: 'bottom center' },
{ label: strings.bottom_right, value: 'bottom right' },
] }
disabled={ ( props.attributes.backgroundImage === 'none' && isNotDisabled ) }
onChange={ ( value ) => handlers.styleAttrChange( 'backgroundPosition', value ) }
/>
) }
</FlexBlock>
</Flex>
{ ( props.attributes.backgroundImage !== 'none' || ! isNotDisabled ) && (
<Flex gap={ 4 } align="flex-start" className={ 'wpforms-gutenberg-form-selector-flex' } justify="space-between">
<FlexBlock>
<SelectControl
label={ strings.repeat }
tabIndex={ tabIndex }
value={ props.attributes.backgroundRepeat }
options={ [
{ label: strings.no_repeat, value: 'no-repeat' },
{ label: strings.tile, value: 'repeat' },
{ label: strings.repeat_x, value: 'repeat-x' },
{ label: strings.repeat_y, value: 'repeat-y' },
] }
disabled={ ( props.attributes.backgroundImage === 'none' && isNotDisabled ) }
onChange={ ( value ) => handlers.styleAttrChange( 'backgroundRepeat', value ) }
/>
</FlexBlock>
<FlexBlock>
<SelectControl
label={ strings.size }
tabIndex={ tabIndex }
value={ props.attributes.backgroundSizeMode }
options={ [
{ label: strings.dimensions, value: 'dimensions' },
{ label: strings.cover, value: 'cover' },
] }
disabled={ ( props.attributes.backgroundImage === 'none' && isNotDisabled ) }
onChange={ ( value ) => app.handleSizeFromDimensions( props, handlers, value ) }
/>
</FlexBlock>
</Flex>
) }
{ ( ( props.attributes.backgroundSizeMode === 'dimensions' && props.attributes.backgroundImage !== 'none' ) || ! isNotDisabled ) && (
<Flex gap={ 4 } align="flex-start" className={ 'wpforms-gutenberg-form-selector-flex' } justify="space-between">
<FlexBlock>
<__experimentalUnitControl
label={ strings.width }
tabIndex={ tabIndex }
value={ props.attributes.backgroundWidth }
isUnitSelectTabbable={ isNotDisabled }
onChange={ ( value ) => app.handleSizeFromWidth( props, handlers, value ) }
/>
</FlexBlock>
<FlexBlock>
<__experimentalUnitControl
label={ strings.height }
tabIndex={ tabIndex }
value={ props.attributes.backgroundHeight }
isUnitSelectTabbable={ isNotDisabled }
onChange={ ( value ) => app.handleSizeFromHeight( props, handlers, value ) }
/>
</FlexBlock>
</Flex>
) }
{ ( ! showBackgroundPreview || props.attributes.backgroundUrl === 'url()' ) && (
( props.attributes.backgroundImage === 'library' && (
<Flex gap={ 4 } align="flex-start" className={ 'wpforms-gutenberg-form-selector-flex' } justify="space-between">
<FlexBlock>
<Button
isSecondary
tabIndex={ tabIndex }
className={ 'wpforms-gutenberg-form-selector-media-library-button' }
onClick={ app.openMediaLibrary.bind( null, props, handlers, setShowBackgroundPreview ) }
>
{ strings.choose_image }
</Button>
</FlexBlock>
</Flex>
) ) || ( props.attributes.backgroundImage === 'stock' && (
<Flex gap={ 4 } align="flex-start" className={ 'wpforms-gutenberg-form-selector-flex' } justify="space-between">
<FlexBlock>
<Button
isSecondary
tabIndex={ tabIndex }
className={ 'wpforms-gutenberg-form-selector-media-library-button' }
onClick={ stockPhotos?.openModal.bind( null, props, handlers, 'bg-styles', setShowBackgroundPreview ) }
>
{ strings.choose_image }
</Button>
</FlexBlock>
</Flex>
) )
) }
{ ( ( showBackgroundPreview && props.attributes.backgroundImage !== 'none' ) || props.attributes.backgroundUrl !== 'url()' ) && (
<Flex gap={ 4 } align="flex-start" className={ 'wpforms-gutenberg-form-selector-flex' } justify="space-between">
<FlexBlock>
<div>
<BackgroundPreview
attributes={ props.attributes }
onRemoveBackground={
() => {
app.onRemoveBackground( setShowBackgroundPreview, handlers, setLastBgImage );
}
}
onPreviewClicked={ () => {
if ( props.attributes.backgroundImage === 'library' ) {
return app.openMediaLibrary( props, handlers, setShowBackgroundPreview );
}
return stockPhotos?.openModal( props, handlers, 'bg-styles', setShowBackgroundPreview );
} }
/>
</div>
<TextControl
label={ strings.image_url }
tabIndex={ tabIndex }
value={ props.attributes.backgroundImage !== 'none' && props.attributes.backgroundUrl }
className={ 'wpforms-gutenberg-form-selector-image-url' }
onChange={ ( value ) => handlers.styleAttrChange( 'backgroundUrl', value ) }
onLoad={ ( value ) => props.attributes.backgroundImage !== 'none' && handlers.styleAttrChange( 'backgroundUrl', value ) }
/>
</FlexBlock>
</Flex>
) }
<Flex gap={ 4 } align="flex-start" className={ 'wpforms-gutenberg-form-selector-flex' } justify="space-between">
<FlexBlock>
<div className="wpforms-gutenberg-form-selector-control-label">{ strings.colors }</div>
<PanelColorSettings
__experimentalIsRenderedInSidebar
enableAlpha
showTitle={ false }
tabIndex={ tabIndex }
className="wpforms-gutenberg-form-selector-color-panel"
colorSettings={ [
{
value: props.attributes.backgroundColor,
onChange: ( value ) => {
if ( ! isNotDisabled ) {
return;
}
handlers.styleAttrChange( 'backgroundColor', value );
},
label: strings.background,
},
] }
/>
</FlexBlock>
</Flex>
</div>
</PanelBody>
);
},
/**
* Open media library modal and handle image selection.
*
* @since 1.8.8
*
* @param {Object} props Block properties.
* @param {Object} handlers Block handlers.
* @param {Function} setShowBackgroundPreview Set show background preview.
*/
openMediaLibrary( props, handlers, setShowBackgroundPreview ) {
const frame = wp.media( {
title: strings.select_background_image,
multiple: false,
library: {
type: 'image',
},
button: {
text: strings.select_image,
},
} );
frame.on( 'select', () => {
const attachment = frame.state().get( 'selection' ).first().toJSON();
const setAttr = {};
const attribute = 'backgroundUrl';
if ( attachment.url ) {
const value = `url(${ attachment.url })`;
setAttr[ attribute ] = value;
props.setAttributes( setAttr );
handlers.styleAttrChange( 'backgroundUrl', value );
setShowBackgroundPreview( true );
}
} );
frame.open();
},
/**
* Set container background image.
*
* @since 1.8.8
*
* @param {HTMLElement} container Container element.
* @param {string} value Value.
*
* @return {boolean} True if the value was set, false otherwise.
*/
setContainerBackgroundImage( container, value ) {
if ( value === 'none' ) {
container.style.setProperty( `--wpforms-background-url`, 'url()' );
}
return true;
},
/**
* Set container background image.
*
* @since 1.8.8
*
* @param {Object} props Block properties.
* @param {Object} handlers Block event handlers.
* @param {string} value Value.
* @param {string} lastBgImage Last background image.
* @param {Function} setLastBgImage Set last background image.
*/
setContainerBackgroundImageWrapper( props, handlers, value, lastBgImage, setLastBgImage ) {
if ( value === 'none' ) {
setLastBgImage( props.attributes.backgroundUrl );
props.attributes.backgroundUrl = 'url()';
handlers.styleAttrChange( 'backgroundUrl', 'url()' );
} else if ( lastBgImage ) {
props.attributes.backgroundUrl = lastBgImage;
handlers.styleAttrChange( 'backgroundUrl', lastBgImage );
}
handlers.styleAttrChange( 'backgroundImage', value );
},
/**
* Set container background position.
*
* @since 1.8.8
*
* @param {HTMLElement} container Container element.
* @param {string} value Value.
*
* @return {boolean} True if the value was set, false otherwise.
*/
setContainerBackgroundPosition( container, value ) {
container.style.setProperty( `--wpforms-background-position`, value );
return true;
},
/**
* Set container background repeat.
*
* @since 1.8.8
*
* @param {HTMLElement} container Container element.
* @param {string} value Value.
*
* @return {boolean} True if the value was set, false otherwise.
*/
setContainerBackgroundRepeat( container, value ) {
container.style.setProperty( `--wpforms-background-repeat`, value );
return true;
},
/**
* Handle real size from dimensions.
*
* @since 1.8.8
*
* @param {Object} props Block properties.
* @param {Object} handlers Block handlers.
* @param {string} value Value.
*/
handleSizeFromDimensions( props, handlers, value ) {
if ( value === 'cover' ) {
props.attributes.backgroundSize = 'cover';
handlers.styleAttrChange( 'backgroundWidth', props.attributes.backgroundWidth );
handlers.styleAttrChange( 'backgroundHeight', props.attributes.backgroundHeight );
handlers.styleAttrChange( 'backgroundSizeMode', 'cover' );
handlers.styleAttrChange( 'backgroundSize', 'cover' );
} else {
props.attributes.backgroundSize = 'dimensions';
handlers.styleAttrChange( 'backgroundSizeMode', 'dimensions' );
handlers.styleAttrChange( 'backgroundSize', props.attributes.backgroundWidth + ' ' + props.attributes.backgroundHeight );
}
},
/**
* Handle real size from width.
*
* @since 1.8.8
*
* @param {Object} props Block properties.
* @param {Object} handlers Block handlers.
* @param {string} value Value.
*/
handleSizeFromWidth( props, handlers, value ) {
props.attributes.backgroundSize = value + ' ' + props.attributes.backgroundHeight;
props.attributes.backgroundWidth = value;
handlers.styleAttrChange( 'backgroundSize', value + ' ' + props.attributes.backgroundHeight );
handlers.styleAttrChange( 'backgroundWidth', value );
},
/**
* Handle real size from height.
*
* @since 1.8.8
*
* @param {Object} props Block properties.
* @param {Object} handlers Block handlers.
* @param {string} value Value.
*/
handleSizeFromHeight( props, handlers, value ) {
props.attributes.backgroundSize = props.attributes.backgroundWidth + ' ' + value;
props.attributes.backgroundHeight = value;
handlers.styleAttrChange( 'backgroundSize', props.attributes.backgroundWidth + ' ' + value );
handlers.styleAttrChange( 'backgroundHeight', value );
},
/**
* Set container background width.
*
* @since 1.8.8
*
* @param {HTMLElement} container Container element.
* @param {string} value Value.
*
* @return {boolean} True if the value was set, false otherwise.
*/
setContainerBackgroundWidth( container, value ) {
container.style.setProperty( `--wpforms-background-width`, value );
return true;
},
/**
* Set container background height.
*
* @since 1.8.8
*
* @param {HTMLElement} container Container element.
* @param {string} value Value.
*
* @return {boolean} True if the value was set, false otherwise.
*/
setContainerBackgroundHeight( container, value ) {
container.style.setProperty( `--wpforms-background-height`, value );
return true;
},
/**
* Set container background url.
*
* @since 1.8.8
*
* @param {HTMLElement} container Container element.
* @param {string} value Value.
*
* @return {boolean} True if the value was set, false otherwise.
*/
setBackgroundUrl( container, value ) {
container.style.setProperty( `--wpforms-background-url`, value );
return true;
},
/**
* Set container background color.
*
* @since 1.8.8
*
* @param {HTMLElement} container Container element.
* @param {string} value Value.
*
* @return {boolean} True if the value was set, false otherwise.
*/
setBackgroundColor( container, value ) {
container.style.setProperty( `--wpforms-background-color`, value );
return true;
},
_showBackgroundPreview( props ) {
return props.attributes.backgroundImage !== 'none' &&
props.attributes.backgroundUrl &&
props.attributes.backgroundUrl !== 'url()';
},
/**
* Remove background image.
*
* @since 1.8.8
*
* @param {Function} setShowBackgroundPreview Set show background preview.
* @param {Object} handlers Block handlers.
* @param {Function} setLastBgImage Set last background image.
*/
onRemoveBackground( setShowBackgroundPreview, handlers, setLastBgImage ) {
setShowBackgroundPreview( false );
handlers.styleAttrChange( 'backgroundUrl', 'url()' );
setLastBgImage( '' );
},
};
return app;
}() );
@@ -0,0 +1,181 @@
/* global wpforms_gutenberg_form_selector */
/* jshint es3: false, esversion: 6 */
/**
* @param strings.border_radius
* @param strings.border_size
* @param strings.button_color_notice
* @param strings.button_styles
* @param strings.dashed
* @param strings.solid
*/
/**
* Gutenberg editor block.
*
* Button styles panel module.
*
* @since 1.8.8
*/
export default ( ( function() {
/**
* WP core components.
*
* @since 1.8.8
*/
const { PanelColorSettings } = wp.blockEditor || wp.editor;
const { SelectControl, PanelBody, Flex, FlexBlock, __experimentalUnitControl } = wp.components;
/**
* Localized data aliases.
*
* @since 1.8.8
*/
const { strings, defaults } = wpforms_gutenberg_form_selector;
// noinspection UnnecessaryLocalVariableJS
/**
* Public functions and properties.
*
* @since 1.8.8
*
* @type {Object}
*/
const app = {
/**
* Get block attributes.
*
* @since 1.8.8
*
* @return {Object} Block attributes.
*/
getBlockAttributes() {
return {
buttonSize: {
type: 'string',
default: defaults.buttonSize,
},
buttonBorderStyle: {
type: 'string',
default: defaults.buttonBorderStyle,
},
buttonBorderSize: {
type: 'string',
default: defaults.buttonBorderSize,
},
buttonBorderRadius: {
type: 'string',
default: defaults.buttonBorderRadius,
},
buttonBackgroundColor: {
type: 'string',
default: defaults.buttonBackgroundColor,
},
buttonTextColor: {
type: 'string',
default: defaults.buttonTextColor,
},
buttonBorderColor: {
type: 'string',
default: defaults.buttonBorderColor,
},
};
},
/**
* Get Button styles JSX code.
*
* @since 1.8.8
*
* @param {Object} props Block properties.
* @param {Object} handlers Block event handlers.
* @param {Object} sizeOptions Size selector options.
* @param {Object} formSelectorCommon Form selector common object.
*
* @return {Object} Button styles JSX code.
*/
getButtonStyles( props, handlers, sizeOptions, formSelectorCommon ) { // eslint-disable-line max-lines-per-function
return (
<PanelBody className={ formSelectorCommon.getPanelClass( props ) } title={ strings.button_styles }>
<Flex gap={ 4 } align="flex-start" className={ 'wpforms-gutenberg-form-selector-flex' } justify="space-between">
<FlexBlock>
<SelectControl
label={ strings.size }
value={ props.attributes.buttonSize }
options={ sizeOptions }
onChange={ ( value ) => handlers.styleAttrChange( 'buttonSize', value ) }
/>
</FlexBlock>
<FlexBlock>
<SelectControl
label={ strings.border }
value={ props.attributes.buttonBorderStyle }
options={
[
{ label: strings.none, value: 'none' },
{ label: strings.solid, value: 'solid' },
{ label: strings.dashed, value: 'dashed' },
{ label: strings.dotted, value: 'dotted' },
]
}
onChange={ ( value ) => handlers.styleAttrChange( 'buttonBorderStyle', value ) }
/>
</FlexBlock>
</Flex>
<Flex gap={ 4 } align="flex-start" className={ 'wpforms-gutenberg-form-selector-flex' } justify="space-between">
<FlexBlock>
<__experimentalUnitControl
label={ strings.border_size }
value={ props.attributes.buttonBorderStyle === 'none' ? '' : props.attributes.buttonBorderSize }
min={ 0 }
disabled={ props.attributes.buttonBorderStyle === 'none' }
onChange={ ( value ) => handlers.styleAttrChange( 'buttonBorderSize', value ) }
isUnitSelectTabbable
/>
</FlexBlock>
<FlexBlock>
<__experimentalUnitControl
onChange={ ( value ) => handlers.styleAttrChange( 'buttonBorderRadius', value ) }
label={ strings.border_radius }
min={ 0 }
isUnitSelectTabbable
value={ props.attributes.buttonBorderRadius } />
</FlexBlock>
</Flex>
<div className="wpforms-gutenberg-form-selector-color-picker">
<div className="wpforms-gutenberg-form-selector-control-label">{ strings.colors }</div>
<PanelColorSettings
__experimentalIsRenderedInSidebar
enableAlpha
showTitle={ false }
className={ formSelectorCommon.getColorPanelClass( props.attributes.buttonBorderStyle ) }
colorSettings={ [
{
value: props.attributes.buttonBackgroundColor,
onChange: ( value ) => handlers.styleAttrChange( 'buttonBackgroundColor', value ),
label: strings.background,
},
{
value: props.attributes.buttonBorderColor,
onChange: ( value ) => handlers.styleAttrChange( 'buttonBorderColor', value ),
label: strings.border,
},
{
value: props.attributes.buttonTextColor,
onChange: ( value ) => handlers.styleAttrChange( 'buttonTextColor', value ),
label: strings.text,
},
] } />
<div className="wpforms-gutenberg-form-selector-legend wpforms-button-color-notice">
{ strings.button_color_notice }
</div>
</div>
</PanelBody>
);
},
};
return app;
} )() );
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,255 @@
/* global wpforms_gutenberg_form_selector */
/* jshint es3: false, esversion: 6 */
/**
* @param strings.border_color
* @param strings.border_style
* @param strings.border_width
* @param strings.container_styles
* @param strings.shadow_size
*/
/**
* Gutenberg editor block.
*
* Container styles panel module.
*
* @since 1.8.8
*/
export default ( ( $ ) => {
/**
* WP core components.
*
* @since 1.8.8
*/
const { PanelColorSettings } = wp.blockEditor || wp.editor;
const { SelectControl, PanelBody, Flex, FlexBlock, __experimentalUnitControl } = wp.components;
/**
* Localized data aliases.
*
* @since 1.8.8
*/
const { strings, defaults } = wpforms_gutenberg_form_selector;
/**
* Public functions and properties.
*
* @since 1.8.8
*
* @type {Object}
*/
const app = {
/**
* Start the engine.
*
* @since 1.8.8
*/
init() {
$( app.ready );
},
/**
* Document ready.
*
* @since 1.8.8
*/
ready() {
app.events();
},
/**
* Events.
*
* @since 1.8.8
*/
events() {
},
/**
* Get block attributes.
*
* @since 1.8.8
*
* @return {Object} Block attributes.
*/
getBlockAttributes() {
return {
containerPadding: {
type: 'string',
default: defaults.containerPadding,
},
containerBorderStyle: {
type: 'string',
default: defaults.containerBorderStyle,
},
containerBorderWidth: {
type: 'string',
default: defaults.containerBorderWidth,
},
containerBorderColor: {
type: 'string',
default: defaults.containerBorderColor,
},
containerBorderRadius: {
type: 'string',
default: defaults.containerBorderRadius,
},
containerShadowSize: {
type: 'string',
default: defaults.containerShadowSize,
},
};
},
/**
* Get Container Styles panel JSX code.
*
* @since 1.8.8
*
* @param {Object} props Block properties.
* @param {Object} handlers Block handlers.
* @param {Object} formSelectorCommon Common form selector functions.
*
* @param {Object} uiState UI state.
*
* @return {Object} Field styles JSX code.
*/
getContainerStyles( props, handlers, formSelectorCommon, uiState ) { // eslint-disable-line max-lines-per-function, complexity
let cssClass = formSelectorCommon.getPanelClass( props );
const isNotDisabled = uiState.isNotDisabled;
const isProEnabled = uiState.isProEnabled;
if ( ! isNotDisabled ) {
cssClass += ' wpforms-gutenberg-panel-disabled';
}
return (
<PanelBody className={ cssClass } title={ strings.container_styles }>
<div // eslint-disable-line jsx-a11y/no-static-element-interactions
className="wpforms-gutenberg-form-selector-panel-body"
onClick={ ( event ) => {
if ( isNotDisabled ) {
return;
}
event.stopPropagation();
if ( ! isProEnabled ) {
return formSelectorCommon.education.showProModal( 'container', strings.container_styles );
}
formSelectorCommon.education.showLicenseModal( 'container', strings.container_styles, 'container-styles' );
} }
onKeyDown={ ( event ) => {
if ( isNotDisabled ) {
return;
}
event.stopPropagation();
if ( ! isProEnabled ) {
return formSelectorCommon.education.showProModal( 'container', strings.container_styles );
}
formSelectorCommon.education.showLicenseModal( 'container', strings.container_styles, 'container-styles' );
} }
>
<Flex gap={ 4 } align="flex-start" className="wpforms-gutenberg-form-selector-flex" justify="space-between">
<FlexBlock>
<__experimentalUnitControl
label={ strings.padding }
tabIndex={ isNotDisabled ? 0 : -1 }
value={ props.attributes.containerPadding }
min={ 0 }
isUnitSelectTabbable={ isNotDisabled }
onChange={ ( value ) => handlers.styleAttrChange( 'containerPadding', value ) }
/>
</FlexBlock>
<FlexBlock>
<SelectControl
label={ strings.border_style }
tabIndex={ isNotDisabled ? 0 : -1 }
value={ props.attributes.containerBorderStyle }
options={ [
{ label: strings.none, value: 'none' },
{ label: strings.solid, value: 'solid' },
{ label: strings.dotted, value: 'dotted' },
{ label: strings.dashed, value: 'dashed' },
{ label: strings.double, value: 'double' },
] }
onChange={ ( value ) => handlers.styleAttrChange( 'containerBorderStyle', value ) }
/>
</FlexBlock>
</Flex>
<Flex gap={ 4 } align="flex-start" className="wpforms-gutenberg-form-selector-flex" justify="space-between">
<FlexBlock>
<__experimentalUnitControl
label={ strings.border_width }
tabIndex={ isNotDisabled ? 0 : -1 }
value={ props.attributes.containerBorderStyle === 'none' ? '' : props.attributes.containerBorderWidth }
min={ 0 }
disabled={ props.attributes.containerBorderStyle === 'none' }
isUnitSelectTabbable={ isNotDisabled }
onChange={ ( value ) => handlers.styleAttrChange( 'containerBorderWidth', value ) }
/>
</FlexBlock>
<FlexBlock>
<__experimentalUnitControl
label={ strings.border_radius }
tabIndex={ isNotDisabled ? 0 : -1 }
value={ props.attributes.containerBorderRadius }
min={ 0 }
isUnitSelectTabbable={ isNotDisabled }
onChange={ ( value ) => handlers.styleAttrChange( 'containerBorderRadius', value ) }
/>
</FlexBlock>
</Flex>
<Flex gap={ 4 } align="flex-start" className="wpforms-gutenberg-form-selector-flex" justify="space-between">
<FlexBlock>
<SelectControl
label={ strings.shadow_size }
tabIndex={ isNotDisabled ? 0 : -1 }
value={ props.attributes.containerShadowSize }
options={ [
{ label: strings.none, value: 'none' },
{ label: strings.small, value: 'small' },
{ label: strings.medium, value: 'medium' },
{ label: strings.large, value: 'large' },
] }
onChange={ ( value ) => handlers.styleAttrChange( 'containerShadowSize', value ) }
/>
</FlexBlock>
</Flex>
<Flex gap={ 4 } align="flex-start" className="wpforms-gutenberg-form-selector-flex" justify="space-between">
<FlexBlock>
<div className="wpforms-gutenberg-form-selector-control-label">{ strings.colors }</div>
<PanelColorSettings
__experimentalIsRenderedInSidebar
enableAlpha
showTitle={ false }
tabIndex={ isNotDisabled ? 0 : -1 }
className={ props.attributes.containerBorderStyle === 'none' ? 'wpforms-gutenberg-form-selector-color-panel wpforms-gutenberg-form-selector-color-panel-disabled' : 'wpforms-gutenberg-form-selector-color-panel' }
colorSettings={ [
{
value: props.attributes.containerBorderColor,
onChange: ( value ) => {
if ( ! isNotDisabled ) {
return;
}
handlers.styleAttrChange( 'containerBorderColor', value );
},
label: strings.border_color,
},
] }
/>
</FlexBlock>
</Flex>
</div>
</PanelBody>
);
},
};
return app;
} )( jQuery );
@@ -0,0 +1,76 @@
/* global wpforms_education, WPFormsEducation */
/**
* WPForms Education Modal module.
*
* @since 1.8.8
*/
export default ( ( $ ) => {
/**
* Public functions and properties.
*
* @since 1.8.8
*
* @type {Object}
*/
const app = {
/**
* Open educational popup for users with no Pro license.
*
* @since 1.8.8
*
* @param {string} panel Panel slug.
* @param {string} feature Feature name.
*/
showProModal( panel, feature ) {
const type = 'pro';
const message = wpforms_education.upgrade[ type ].message_plural.replace( /%name%/g, feature );
const utmContent = {
container: 'Upgrade to Pro - Container Styles',
background: 'Upgrade to Pro - Background Styles',
themes: 'Upgrade to Pro - Themes',
};
$.alert( {
backgroundDismiss: true,
title: feature + ' ' + wpforms_education.upgrade[ type ].title_plural,
icon: 'fa fa-lock',
content: message,
boxWidth: '550px',
theme: 'modern,wpforms-education',
closeIcon: true,
onOpenBefore: function() { // eslint-disable-line object-shorthand
this.$btnc.after( '<div class="discount-note">' + wpforms_education.upgrade_bonus + '</div>' );
this.$btnc.after( wpforms_education.upgrade[ type ].doc.replace( /%25name%25/g, 'AP - ' + feature ) );
this.$body.find( '.jconfirm-content' ).addClass( 'lite-upgrade' );
},
buttons: {
confirm: {
text: wpforms_education.upgrade[ type ].button,
btnClass: 'btn-confirm',
keys: [ 'enter' ],
action: () => {
window.open( WPFormsEducation.core.getUpgradeURL( utmContent[ panel ], type ), '_blank' );
WPFormsEducation.core.upgradeModalThankYou( type );
},
},
},
} );
},
/**
* Open license modal.
*
* @since 1.8.8
*
* @param {string} feature Feature name.
* @param {string} fieldName Field name.
* @param {string} utmContent UTM content.
*/
showLicenseModal( feature, fieldName, utmContent ) {
WPFormsEducation.proCore.licenseModal( feature, fieldName, utmContent );
},
};
return app;
} )( jQuery );
@@ -0,0 +1,189 @@
/* global wpforms_gutenberg_form_selector */
/* jshint es3: false, esversion: 6 */
/**
* @param strings.field_styles
* @param strings.lead_forms_panel_notice_head
* @param strings.lead_forms_panel_notice_text
* @param strings.learn_more
* @param strings.use_modern_notice_head
* @param strings.use_modern_notice_link
* @param strings.use_modern_notice_text
*/
/**
* Gutenberg editor block.
*
* Field styles panel module.
*
* @since 1.8.8
*/
export default ( ( function() {
/**
* WP core components.
*
* @since 1.8.8
*/
const { PanelColorSettings } = wp.blockEditor || wp.editor;
const { SelectControl, PanelBody, Flex, FlexBlock, __experimentalUnitControl } = wp.components;
/**
* Localized data aliases.
*
* @since 1.8.8
*/
const { strings, defaults } = wpforms_gutenberg_form_selector;
// noinspection UnnecessaryLocalVariableJS
/**
* Public functions and properties.
*
* @since 1.8.8
*
* @type {Object}
*/
const app = {
/**
* Get block attributes.
*
* @since 1.8.8
*
* @return {Object} Block attributes.
*/
getBlockAttributes() {
return {
fieldSize: {
type: 'string',
default: defaults.fieldSize,
},
fieldBorderStyle: {
type: 'string',
default: defaults.fieldBorderStyle,
},
fieldBorderSize: {
type: 'string',
default: defaults.fieldBorderSize,
},
fieldBorderRadius: {
type: 'string',
default: defaults.fieldBorderRadius,
},
fieldBackgroundColor: {
type: 'string',
default: defaults.fieldBackgroundColor,
},
fieldBorderColor: {
type: 'string',
default: defaults.fieldBorderColor,
},
fieldTextColor: {
type: 'string',
default: defaults.fieldTextColor,
},
fieldMenuColor: {
type: 'string',
default: defaults.fieldMenuColor,
},
};
},
/**
* Get Field styles JSX code.
*
* @since 1.8.8
*
* @param {Object} props Block properties.
* @param {Object} handlers Block event handlers.
* @param {Object} sizeOptions Size selector options.
* @param {Object} formSelectorCommon Form selector common object.
*
* @return {Object} Field styles JSX code.
*/
getFieldStyles( props, handlers, sizeOptions, formSelectorCommon ) { // eslint-disable-line max-lines-per-function
return (
<PanelBody className={ formSelectorCommon.getPanelClass( props ) } title={ strings.field_styles }>
<Flex gap={ 4 } align="flex-start" className={ 'wpforms-gutenberg-form-selector-flex' } justify="space-between">
<FlexBlock>
<SelectControl
label={ strings.size }
value={ props.attributes.fieldSize }
options={ sizeOptions }
onChange={ ( value ) => handlers.styleAttrChange( 'fieldSize', value ) }
/>
</FlexBlock>
<FlexBlock>
<SelectControl
label={ strings.border }
value={ props.attributes.fieldBorderStyle }
options={
[
{ label: strings.none, value: 'none' },
{ label: strings.solid, value: 'solid' },
{ label: strings.dashed, value: 'dashed' },
{ label: strings.dotted, value: 'dotted' },
]
}
onChange={ ( value ) => handlers.styleAttrChange( 'fieldBorderStyle', value ) }
/>
</FlexBlock>
</Flex>
<Flex gap={ 4 } align="flex-start" className={ 'wpforms-gutenberg-form-selector-flex' } justify="space-between">
<FlexBlock>
<__experimentalUnitControl
label={ strings.border_size }
value={ props.attributes.fieldBorderStyle === 'none' ? '' : props.attributes.fieldBorderSize }
min={ 0 }
disabled={ props.attributes.fieldBorderStyle === 'none' }
onChange={ ( value ) => handlers.styleAttrChange( 'fieldBorderSize', value ) }
isUnitSelectTabbable
/>
</FlexBlock>
<FlexBlock>
<__experimentalUnitControl
label={ strings.border_radius }
value={ props.attributes.fieldBorderRadius }
min={ 0 }
isUnitSelectTabbable
onChange={ ( value ) => handlers.styleAttrChange( 'fieldBorderRadius', value ) }
/>
</FlexBlock>
</Flex>
<div className="wpforms-gutenberg-form-selector-color-picker">
<div className="wpforms-gutenberg-form-selector-control-label">{ strings.colors }</div>
<PanelColorSettings
__experimentalIsRenderedInSidebar
enableAlpha
showTitle={ false }
className={ formSelectorCommon.getColorPanelClass( props.attributes.fieldBorderStyle ) }
colorSettings={ [
{
value: props.attributes.fieldBackgroundColor,
onChange: ( value ) => handlers.styleAttrChange( 'fieldBackgroundColor', value ),
label: strings.background,
},
{
value: props.attributes.fieldBorderColor,
onChange: ( value ) => handlers.styleAttrChange( 'fieldBorderColor', value ),
label: strings.border,
},
{
value: props.attributes.fieldTextColor,
onChange: ( value ) => handlers.styleAttrChange( 'fieldTextColor', value ),
label: strings.text,
},
{
value: props.attributes.fieldMenuColor,
onChange: ( value ) => handlers.styleAttrChange( 'fieldMenuColor', value ),
label: strings.menu,
},
] }
/>
</div>
</PanelBody>
);
},
};
return app;
} )() );
File diff suppressed because it is too large Load Diff