Plugin Directory

Changeset 3471571


Ignore:
Timestamp:
02/28/2026 11:05:25 AM (11 days ago)
Author:
griffinforms
Message:

Release 2.3.3.0

Location:
griffinforms-form-builder/trunk
Files:
13 edited

Legend:

Unmodified
Added
Removed
  • griffinforms-form-builder/trunk/admin/app/html/widgets/presentationarea/formcontrols/inputaddress.php

    r3394319 r3471571  
    4848        echo '<div class="input-group gf-address-admin-city-postal">';
    4949        echo '<div class="form-floating flex-grow-1">';
    50         echo '<input type="text" id="' . esc_attr($this->getFieldControlId('city')) . '" class="form-control gf-address-admin-city"' . $text_attributes . ' placeholder=" ">';
     50        echo '<input type="text" id="' . esc_attr($this->getFieldControlId('city')) . '" class="form-control gf-address-admin-city"' . $text_attributes . ' style="border-top-right-radius:0 !important;border-bottom-right-radius:0 !important;" placeholder=" ">';
    5151        echo '<label>' . esc_html__('City', 'griffinforms-form-builder') . '</label>';
    5252        echo '</div>';
    5353        echo '<div class="form-floating gf-address-admin-postal">';
    54         echo '<input type="text" id="' . esc_attr($this->getFieldControlId('postal-code')) . '" class="form-control"' . $text_attributes . ' placeholder=" ">';
     54        echo '<input type="text" id="' . esc_attr($this->getFieldControlId('postal-code')) . '" class="form-control"' . $text_attributes . ' style="border-top-left-radius:0 !important;border-bottom-left-radius:0 !important;" placeholder=" ">';
    5555        echo '<label>' . esc_html__('Postal Code', 'griffinforms-form-builder') . '</label>';
    5656        echo '</div>';
  • griffinforms-form-builder/trunk/admin/css/app/griffinforms-app-formlayout.css

    r3456321 r3471571  
    459459/* Adds animation to newly added form elements to make them standout. */
    460460.griffinforms-new-formelement {
    461     animation: new-fe 2s ease-out;
    462 }
    463 
    464 @keyframes new-fe {
    465     from {opacity: 0;}
    466     to{opacity: 1;}
     461    transform-origin: center top;
     462    animation: gf-element-insert 560ms cubic-bezier(0.2, 0.85, 0.25, 1) both;
     463    will-change: transform, opacity, box-shadow;
     464}
     465
     466@keyframes gf-element-insert {
     467    0% {
     468        opacity: 0;
     469        transform: translateY(8px) scale(0.97);
     470        box-shadow: 0 0 0 0 var(--gf-color-primary-rgba-18);
     471    }
     472    45% {
     473        opacity: 1;
     474        transform: translateY(-2px) scale(1.01);
     475        box-shadow: 0 0 0 3px var(--gf-color-primary-rgba-18);
     476    }
     477    100% {
     478        opacity: 1;
     479        transform: translateY(0) scale(1);
     480        box-shadow: 0 0 0 0 var(--gf-color-primary-rgba-18);
     481    }
     482}
     483
     484@media (prefers-reduced-motion: reduce) {
     485    .griffinforms-new-formelement {
     486        animation: gf-element-insert-reduced 160ms ease-out both;
     487    }
     488}
     489
     490@keyframes gf-element-insert-reduced {
     491    from {
     492        opacity: 0;
     493    }
     494    to {
     495        opacity: 1;
     496    }
    467497}
    468498
     
    743773.gf-address-admin-city-postal {
    744774    gap: 0;
    745 }
    746 
    747 .gf-address-admin-city-postal .gf-address-admin-city {
     775    align-items: stretch;
     776}
     777
     778.gf-address-admin-city-postal > .form-floating.flex-grow-1 > .gf-address-admin-city {
     779    border-top-left-radius: var(--bs-border-radius, 0.375rem) !important;
     780    border-bottom-left-radius: var(--bs-border-radius, 0.375rem) !important;
    748781    border-top-right-radius: 0 !important;
    749782    border-bottom-right-radius: 0 !important;
    750783}
    751784
    752 .gf-address-admin-city-postal .gf-address-admin-postal {
     785.gf-address-admin-city-postal > .form-floating.gf-address-admin-postal {
    753786    flex: 0 0 200px;
    754787}
    755788
    756 .gf-address-admin-city-postal .gf-address-admin-postal .form-control {
     789.gf-address-admin-city-postal > .form-floating.gf-address-admin-postal > .form-control {
    757790    border-top-left-radius: 0 !important;
    758791    border-bottom-left-radius: 0 !important;
     792    border-top-right-radius: var(--bs-border-radius, 0.375rem) !important;
     793    border-bottom-right-radius: var(--bs-border-radius, 0.375rem) !important;
     794}
     795
     796.gf-address-admin-city-postal > .form-floating > .form-control:focus {
     797    position: relative;
     798    z-index: 2;
    759799}
    760800
  • griffinforms-form-builder/trunk/admin/css/griffinforms-lists.css

    r3394319 r3471571  
    2929.griffinforms-container .error a {
    3030    text-decoration: none !important;
     31}
     32
     33.gf-delete-modal-alert {
     34    border: 1px solid #f1aeb5;
     35    background: #f8d7da;
     36    color: #842029;
     37    padding: 8px 10px;
     38    border-radius: 6px;
     39    font-size: 12px;
     40}
     41
     42.gf-delete-modal-warning {
     43    display: flex;
     44    align-items: flex-start;
     45    gap: 8px;
     46    border: 1px solid #f1aeb5;
     47    background: #fff5f5;
     48    color: #842029;
     49    border-radius: 8px;
     50    padding: 10px;
     51}
     52
     53.gf-delete-modal-warning .dashicons {
     54    margin-top: 1px;
     55}
     56
     57.gf-delete-modal-warning-title {
     58    font-weight: 600;
     59    line-height: 1.2;
     60}
     61
     62.gf-delete-modal-warning-description {
     63    margin-top: 2px;
     64    font-size: 12px;
     65    color: #8a3a44;
     66}
     67
     68.gf-delete-modal-message {
     69    color: #1d2327;
     70}
     71
     72.gf-delete-modal-content label[for="gf-also-delete-entries"] {
     73    display: inline-flex;
     74    align-items: flex-start;
     75    gap: 8px;
     76}
     77
     78.gf-delete-modal-content #gf-also-delete-entries {
     79    margin-top: 3px;
    3180}
    3281
  • griffinforms-form-builder/trunk/admin/html/modals/deletelistitem.php

    r3299683 r3471571  
    1919    protected function modalContent()
    2020    {
    21         echo '<div class="p-3">';
     21        echo '<div class="p-3 gf-delete-modal-content">';
    2222
    23         echo '<div class="mb-2">';
    24         echo '<div class="modal-alert text-danger small mt-2" style="display:none;"></div>';
     23        echo '<div class="mb-3">';
     24        echo '<div class="modal-alert gf-delete-modal-alert" role="alert" style="display:none;"></div>';
    2525        echo '</div>';
    2626
    27         echo '<div class="mb-2 fw-bold">';
     27        echo '<div class="gf-delete-modal-warning mb-3">';
     28        echo '<span class="dashicons dashicons-warning"></span>';
     29        echo '<div class="gf-delete-modal-warning-copy">';
     30        echo '<div class="gf-delete-modal-warning-title">' . esc_html__('This action cannot be undone.', 'griffinforms-form-builder') . '</div>';
     31        echo '<div class="gf-delete-modal-warning-description">' . esc_html__('Please confirm you want to continue.', 'griffinforms-form-builder') . '</div>';
     32        echo '</div>';
     33        echo '</div>';
     34
     35        echo '<div class="mb-2 fw-semibold gf-delete-modal-message">';
    2836        $this->confirmationText();
    2937        echo '</div>';
    3038
    31         echo '<div class="">';
     39        echo '<div class="mt-3">';
    3240        $this->additionalOptions();
    3341        echo '</div>';
     
    5361        echo '<div class="modal-footer border-top-0">';
    5462        $this->cancelBtn();
    55         $this->ActionBtn('delete', 'primary');
     63        $this->ActionBtn('delete', 'danger');
    5664        echo '</div>'; 
    5765    }
     
    6674           
    6775            modal.addEventListener("show.bs.modal", function (event) {
     76                $(".modal-alert").hide().text("");
    6877                $("#<?php echo esc_attr($this->action_btn_id); ?>").attr("data-bs-dismiss", "");
    6978                const triggerElement = event.relatedTarget;
    70                 let itemId = parseInt(triggerElement.getAttribute("data-gf-id"));
     79                if (!triggerElement) {
     80                    return;
     81                }
     82                let itemId = parseInt(triggerElement.getAttribute("data-gf-id"), 10);
    7183                $("#<?php echo esc_attr($this->action_btn_id); ?>").attr("data-gf-itemid", itemId);
    7284
     
    7486           
    7587            $("#<?php echo esc_js($this->action_btn_id); ?>").click(function() {
     88                const $actionBtn = $(this);
     89                if ($actionBtn.prop("disabled")) {
     90                    return;
     91                }
     92                $actionBtn.prop("disabled", true).text("<?php echo esc_js(__('Deleting...', 'griffinforms-form-builder')); ?>");
    7693                let alsoDeleteSubmissions = 0;
    7794
     
    95112                    } else {
    96113                        $(".modal-alert").text("<?php echo esc_js($this->lang->getText('delete_failed', 'modal_text')); ?>").show();
     114                        $actionBtn.prop("disabled", false).text("<?php echo esc_js($this->lang->getText('delete', 'action_btn')); ?>");
    97115                    }
    98116                }).fail(function() {
    99117                    $(".modal-alert").text("<?php echo esc_js($this->lang->getText('ajax_request_failed', 'modal_text')); ?>").show();
     118                    $actionBtn.prop("disabled", false).text("<?php echo esc_js($this->lang->getText('delete', 'action_btn')); ?>");
    100119                });
    101120            });
  • griffinforms-form-builder/trunk/admin/language/forms.php

    r3446601 r3471571  
    167167    protected function deleteFormSubmissionsModalText()
    168168    {
    169         return __('Also delete submissions for this form?', 'griffinforms-form-builder');
     169        return __('Also permanently delete all submissions for this form?', 'griffinforms-form-builder');
    170170    }
    171171
     
    177177    protected function deleteListItemModalText()
    178178    {
    179         return __('Do you wish to delete this form? This action is irreversible.', 'griffinforms-form-builder');
     179        return __('Delete this form permanently? This action cannot be undone.', 'griffinforms-form-builder');
    180180    }
    181181   
  • griffinforms-form-builder/trunk/admin/language/lists.php

    r3303055 r3471571  
    105105    protected function deleteListItemModalText()
    106106    {
    107         return __('Do you wish to delete this item? This action is irreversible.', 'griffinforms-form-builder');
     107        return __('Delete this item permanently? This action cannot be undone.', 'griffinforms-form-builder');
    108108    }
    109109
  • griffinforms-form-builder/trunk/admin/language/messages.php

    r3299683 r3471571  
    3434    protected function deleteListItemModalText()
    3535    {
    36         return __('Do you wish to delete this message? This action is irreversible.', 'griffinforms-form-builder');
     36        return __('Delete this message permanently? This action cannot be undone.', 'griffinforms-form-builder');
    3737    }
    3838   
  • griffinforms-form-builder/trunk/admin/language/submissions.php

    r3455761 r3471571  
    132132    protected function deleteListItemModalText()
    133133    {
    134         return __('Do you wish to delete this form submission? This action is irreversible.', 'griffinforms-form-builder');
     134        return __('Delete this submission permanently? This action cannot be undone.', 'griffinforms-form-builder');
    135135    }
    136136
  • griffinforms-form-builder/trunk/admin/sql/format.php

    r3461374 r3471571  
    138138        $update = $wpdb->update($table, $data, $where);
    139139        return $update;
     140    }
     141
     142    /**
     143     * Remove a child ID from a parent's serialized children column.
     144     *
     145     * Shared by delete cascade methods (formpage -> pagerow -> rowcolumn -> field).
     146     *
     147     * @param string $parent_type   Parent item type/table key.
     148     * @param int    $parent_id     Parent item ID.
     149     * @param string $children_type Parent column that stores child IDs.
     150     * @param int    $child_id      Child ID to remove from the list.
     151     * @return bool True when relation is synchronized or parent is unavailable.
     152     */
     153    protected function updateChildren($parent_type, $parent_id, $children_type, $child_id)
     154    {
     155        $parent_id = absint($parent_id);
     156        $child_id = absint($child_id);
     157
     158        // When parent context is unavailable, nothing to sync.
     159        if ($parent_id < 1) {
     160            return true;
     161        }
     162
     163        $children_ids_raw = $this->getItemValue($parent_id, $children_type, $parent_type);
     164        $children_ids = $this->maybeDecode($children_ids_raw);
     165
     166        if (!is_array($children_ids)) {
     167            $children_ids = array();
     168        }
     169
     170        $children_ids = array_values(array_filter(array_map('absint', $children_ids), static function ($id) {
     171            return $id > 0;
     172        }));
     173
     174        $updated_children = array_values(array_filter($children_ids, static function ($id) use ($child_id) {
     175            return $id !== $child_id;
     176        }));
     177
     178        $result = $this->updateItemValue($parent_type, $parent_id, $children_type, maybe_serialize($updated_children));
     179        return $result !== false;
    140180    }
    141181   
  • griffinforms-form-builder/trunk/blocks/gutenberg/index.js

    r3450618 r3471571  
    5454    };
    5555
    56     const buildPreviewHtml = (structure, customSubmitLabel, activePage) => {
     56    const scopeThemeCss = (themeCss, previewScopeClass) => {
     57        if (!themeCss || !previewScopeClass) {
     58            return themeCss || '';
     59        }
     60
     61        const scopedSelector = '.' + String(previewScopeClass);
     62        return String(themeCss).replace(/\.griffinforms-block-preview\b/g, scopedSelector);
     63    };
     64
     65    const buildPreviewHtml = (structure, customSubmitLabel, activePage, previewScopeClass) => {
    5766        if (!structure || typeof structure !== 'object') {
    5867            return '';
     
    227236
    228237        const formStyle = structure.form_style_css ? ' style="' + esc(structure.form_style_css) + '"' : '';
    229         const previewClass = 'griffinforms-block-preview' + (structure.theme_css ? ' gf-bp-theme' : ' gf-bp-no-theme');
     238        const previewClass = 'griffinforms-block-preview' + (structure.theme_css ? ' gf-bp-theme' : ' gf-bp-no-theme') + (previewScopeClass ? ' ' + previewScopeClass : '');
    230239        let html = '<div class="' + previewClass + '"' + formStyle + '>';
    231240
    232241        if (structure.theme_css) {
    233             html += '<style class="gf-bp-theme">' + structure.theme_css + '</style>';
     242            html += '<style class="gf-bp-theme">' + scopeThemeCss(structure.theme_css, previewScopeClass) + '</style>';
    234243        }
    235244
     
    362371
    363372    const FormBlockEdit = (props) => {
    364         const { attributes, setAttributes } = props;
     373        const { attributes, setAttributes, clientId } = props;
    365374        const [forms, setForms] = useState([]);
    366375        const [loadingForms, setLoadingForms] = useState(true);
     
    371380        const [activePage, setActivePage] = useState(0);
    372381        const previewRef = useRef(null);
     382        const previewScopeClass = 'gf-bp-scope-' + String(clientId || '').replace(/[^a-zA-Z0-9_-]/g, '');
    373383
    374384        const blockProps = useBlockProps({ className: 'griffinforms-block-editor' });
     
    393403            if (structure) {
    394404                const pageIndex = Math.min(activePage, Math.max(0, (structure.pages || []).length - 1));
    395                 setPreviewHtml(buildPreviewHtml(structure, null, pageIndex));
     405                setPreviewHtml(buildPreviewHtml(structure, null, pageIndex, previewScopeClass));
    396406                ensureThemeFonts(structure.theme_fonts || []);
    397407            }
    398         }, [structure, activePage]);
     408        }, [structure, activePage, previewScopeClass]);
    399409
    400410        // Wire up navigation clicks inside the preview markup
     
    448458                    setStructure(resp);
    449459                    setActivePage(0);
    450                     setPreviewHtml(buildPreviewHtml(resp, null, 0));
     460                    setPreviewHtml(buildPreviewHtml(resp, null, 0, previewScopeClass));
    451461                    const selected = (forms || []).find((f) => parseInt(f.id, 10) === parseInt(formId, 10));
    452462                    setAttributes({
  • griffinforms-form-builder/trunk/config.php

    r3461374 r3471571  
    55class Config
    66{
    7     public const VERSION = '2.3.2.0';
    8     public const DB_VER = '2.3.2.0';
     7    public const VERSION = '2.3.3.0';
     8    public const DB_VER = '2.3.3.0';
    99    public const PHP_REQUIRED = '8.2';
    1010    public const WP_REQUIRED = '6.2';
  • griffinforms-form-builder/trunk/griffinforms.php

    r3461374 r3471571  
    44 * Plugin URI:        https://griffinforms.com/
    55 * Description:       A powerful and flexible form builder for WordPress. Create multi-page forms with drag-and-drop ease, custom validations, and full submission management.
    6  * Version:           2.3.2.0
     6 * Version:           2.3.3.0
    77 * Requires at least: 6.6
    88 * Requires PHP:      8.2
  • griffinforms-form-builder/trunk/readme.txt

    r3461374 r3471571  
    55Tested up to: 6.9
    66Requires PHP: 8.2
    7 Stable tag: 2.3.2.0
     7Stable tag: 2.3.3.0
    88License: GPLv2 or later
    99License URI: https://www.gnu.org/licenses/gpl-2.0.html
     
    174174== Changelog ==
    175175
     176= 2.3.3.0 – 2026-02-28 =
     177* Fix: Isolated Gutenberg block preview theme CSS per block instance so multiple GriffinForms blocks on one page keep independent theme rendering.
     178* Fix: Corrected address field city/postal joined-input corner behavior in builder preview so adjoining borders remain square.
     179* Improvement: Refined list delete confirmation modals with clearer destructive-action hierarchy and improved guard/error behavior.
     180* Fix: Resolved bulk form delete fatal error by adding shared child-relation sync support used by delete cascades.
     181* Improvement: Replaced passive builder new-element fade with a clearer insertion animation and reduced-motion fallback.
     182
    176183= 2.3.2.0 – 2026-02-14 =
    177184* Fix: Added themed/no-theme validation parity so invalid controls are clearly highlighted in themed frontend forms.
     
    211218
    212219== Upgrade Notice ==
     220
     221= 2.3.3.0 =
     222Focused quality patch for Gutenberg multi-block theme isolation, address field joined-input edge consistency, delete-flow reliability, and clearer builder insertion feedback. Recommended update.
    213223
    214224= 2.3.2.0 =
Note: See TracChangeset for help on using the changeset viewer.