wp-shortcake / shortcake

Shortcake makes using WordPress shortcodes a piece of cake.
GNU General Public License v2.0
664 stars 142 forks source link

Repeatable fields #244

Open danielbachhuber opened 9 years ago

danielbachhuber commented 9 years ago

Request from WordPress.org:

A repeater field would be for making an element with X number of parts to it. Like tabs for example.

mattheu commented 9 years ago

Going to punt this from 0.3.

StaggerLeee commented 9 years ago

So it means we could use Shortcake as best possible visual tables editing TinyMce tool ? If it is so it is revolutionary.

widoz commented 8 years ago

Any news on this?

kaoquan commented 7 years ago

I created a new field attribute fields (I'm using richtext plugin as well but you can remove the "shortcake-richtext" class from the textarea https://wordpress.org/plugins/shortcode-ui-richtext/)

This has a bug, it will not update the last keypress or if you do only image add.

You have to create a class with the code below:


/**
 * Primary controller class for Shortcake Columns Field
 */
class Shortcake_Field_Columns {

    /**
     * Shortcake Columns Field controller instance.
     *
     * @access private
     * @var object
     */
    private static $instance;

    /**
     * All registered post fields.
     *
     * @access private
     * @var array
     */
    private $post_fields  = array();

    /**
     * Settings for the Columns Field.
     *
     * @access private
     * @var array
     */
    private $fields = array(
        'columns' => array(
            'template' => 'fusion-shortcake-field-columns',
            'view'     => 'editAttributeField',
        ),
    );

    /**
     * Get instance of Shortcake Columns Field controller.
     *
     * Instantiates object on the fly when not already loaded.
     *
     * @return object
     */
    public static function get_instance() {
        if ( ! isset( self::$instance ) ) {
            self::$instance = new self;
            self::$instance->setup_actions();
        }
        return self::$instance;
    }

    /**
     * Set up actions needed for Columns Field
     */
    private function setup_actions() {
        add_filter( 'shortcode_ui_fields', array( $this, 'filter_shortcode_ui_fields' ) );
        add_action( 'shortcode_ui_loaded_editor', array( $this, 'load_template' ) );
    }

    /**
     * Whether or not the color attribute is present in registered shortcode UI
     *
     * @return bool
     */
    private function columns_attribute_present() {

        foreach ( Shortcode_UI::get_instance()->get_shortcodes() as $shortcode ) {

            if ( empty( $shortcode['attrs'] ) ) {
                continue;
            }

            foreach ( $shortcode['attrs'] as $attribute ) {
                if ( empty( $attribute['type'] ) ) {
                    continue;
                }

                if ( 'columns' === $attribute['type'] ) {
                    return true;
                }
            }
        }

        return false;
    }

    /**
     * Add Color Field settings to Shortcake fields
     *
     * @param array $fields
     * @return array
     */
    public function filter_shortcode_ui_fields( $fields ) {
        return array_merge( $fields, $this->fields );
    }

    /**
     * Output templates used by the Columns field.
     */
    public function load_template() {

        ?>
        <script type="text/javascript">
            function updateColumnData(id){
                var data = [];

                jQuery.each(jQuery('#'+id+"_list > li"), function(index, value){
                    var columnData = {
                        title: htmlspecialchars(jQuery(value).find('.r_title').val()),
                        text_content: htmlspecialchars(jQuery(value).find('.r_text_content').val()),
                        imgid: jQuery(value).find('.imagedata .r_imageid').val(),
                        imgurl: jQuery(value).find('.imagedata .r_imageurl').val()
                    };
                    data.push(columnData);
                });

                var dataText = JSON.stringify(data);
                dataText = dataText.replace('[','').replace(']','').replace(/[\\"']/g, '\'').replace(/\u0000/g, '\'');
                jQuery('#'+id).val((dataText));
            }

            function checkColumns(id){
                if(jQuery('#'+id+"_list > li").size > 15){
                    jQuery('#'+id+"_add_review").hide();
                } else {
                    jQuery('#'+id+"_add_review").show();
                }
            }

            function addColumn(id){
                var list = jQuery('#'+id+'_list');
                var first = jQuery('#'+id+'_first');
                var newElement = first.clone();
                newElement.attr('id','');
                jQuery('input[type=text]', newElement).val('');
                jQuery('.previewimage', newElement).css('background-image','none');
                jQuery('.removeimage', newElement).hide();
                jQuery('input[type=hidden]', newElement).val('');
                jQuery('input[type=number]', newElement).val('');
                jQuery('textarea', newElement).val('');

                jQuery(newElement).find('.shortcake-richtext').summernote( 'destroy' );
                jQuery(newElement).find('.note-editor').remove();
                jQuery(newElement).find('.shortcake-richtext').summernote({
                    toolbar: [
                        [ 'style', ['style'] ],
                        [ 'para', [ 'ul', 'ol' ] ],
                        [ 'font', [ 'bold', 'italic', 'underline', 'strikethrough', 'superscript', 'subscript', 'clear' ] ],
                        [ 'fontsize' , [ 'fontsize' ] ],
                        [ 'color', [ 'color' ] ],
                        [ 'table', [ 'table' ] ],
                        [ 'insert', [ 'link', 'picture', 'video' ] ],
                        [ 'view', [ 'codeview', 'help' ] ]
                    ]
                });
                list.append(newElement);
                checkColumns(id);
            }

            function removeColumn(id,element){
                var parent = jQuery(element).parent('li');
                if(jQuery('#'+id+'_list > li').length > 1){
                    if(parent.attr('id') == id+'_first') {
                        parent.next().attr('id', id+'_first');
                    }
                    parent.remove();
                    updateColumnData(id);
                }
                checkColumns(id);
            }

            function initColumns(id){

                var initdata = jQuery('#'+ id +'_initdata').val();
                initdata = '[' + initdata.replace(/[\\']/g, '"') + ']';
                initdata = JSON.parse(initdata);

                if(initdata.length){
                    for(var i = 0; i < initdata.length; i++){
                        var eData = initdata[i];
                        var element = jQuery('#'+id+'_first');
                        var target;
                        if(i == 0){
                            target = element;
                        } else {
                            target = element.clone();
                            jQuery('input[type=text]', target).val('');
                            jQuery('.previewimage', target).css('background-image','none');
                            jQuery('.removeimage', target).hide();
                            jQuery('input[type=hidden]', target).val('');
                            jQuery('input[type=number]', target).val('');
                            jQuery('textarea', target).val('');

                            jQuery(target).find('.shortcake-richtext').summernote( 'destroy' );
                            jQuery(target).find('.note-editor').remove();
                            jQuery(target).find('.shortcake-richtext').summernote({
                                toolbar: [
                                    [ 'style', ['style'] ],
                                    [ 'para', [ 'ul', 'ol' ] ],
                                    [ 'font', [ 'bold', 'italic', 'underline', 'strikethrough', 'superscript', 'subscript', 'clear' ] ],
                                    [ 'fontsize' , [ 'fontsize' ] ],
                                    [ 'color', [ 'color' ] ],
                                    [ 'table', [ 'table' ] ],
                                    [ 'insert', [ 'link', 'picture', 'video' ] ],
                                    [ 'view', [ 'codeview', 'help' ] ]
                                ]
                            });
                        }

                        if(eData.title){
                            jQuery(target).find('.r_title').val(htmlspecialchars_decode(eData.title));
                        }
                        if(eData.text_content){
                            jQuery(target).find('.r_text_content').val(htmlspecialchars_decode(eData.text_content));
                        }
                        if(eData.imgid){
                            jQuery(target).find('.imagedata .r_imageid').val(eData.imgid);
                            jQuery(target).find('.imagedata .removeimage').show();
                        }
                        if(eData.imgurl){
                            jQuery(target).find('.imagedata .r_imageurl').val(eData.imgurl);
                            jQuery(target).find('.imagedata .previewimage').css('background-image','url(' +eData.imgurl + ')');
                        }

                        if(i != 0){
                            var list = jQuery('#'+id+'_list');
                            target.attr('id','');
                            list.append(target);
                        }
                    }
                }
                updateColumnData(id);
            }

            function attachColumnImage(id,element){

                var meta_image_frame;

                if ( meta_image_frame ) {
                    wp.media.editor.open();
                    return;
                }

                meta_image_frame = wp.media.frames.meta_image_frame = wp.media({
                    title: '<?php  echo __('Select Image', 'xapt') ?>',
                    button: { text:  '<?php  echo __('Insert', 'xapt') ?>' },
                    library: { type: 'image' }
                });

                meta_image_frame.on('select', function(){
                    var media_attachment = meta_image_frame.state().get('selection').first().toJSON();
                    var parent = jQuery(element).parents('.imagecontainer');
                    parent.find('.r_imageid').val(media_attachment.id);
                    parent.find('.r_imageurl').val((media_attachment.sizes && media_attachment.sizes.full.url || media_attachment.url));
                    parent.find('.previewimage').css('background-image','url(' + (media_attachment.sizes && media_attachment.sizes.thumbnail.url || media_attachment.url) + ')');
                    parent.find('.removeimage').show();

                    updateColumnData(id);
                });

                meta_image_frame.open()

            }

            function removeColumnImage(id,elem){
                var parent = jQuery(elem).parents('.imagecontainer');
                parent.find('.r_imageid').val('');
                parent.find('.r_imageurl').val('');
                parent.find('.previewimage').css('background-image','none');
                parent.find('.removeimage').hide();

                updateColumnData(id);
            }

        </script>

        <script type="text/html" id="tmpl-fusion-shortcake-field-columns">
            <div class="field-block shortcode-ui-field-columns shortcode-ui-attribute-{{ data.attr }}">
                <input type="button" class="button-primary" value="<?php  echo __('Add Column', 'xapt') ?>" id="{{ data.id }}_add_review" onclick="addColumn('{{ data.id }}')" />

                <hr />

                <form id="{{ data.id }}_reviews" name="{{ data.id }}_counters">

                    <input type="hidden" class="large-text" name="initdata" id="{{ data.id }}_initdata" value="{{ data.value }}" />

                    <input type="hidden" class="large-text" name="{{ data.attr }}" id="{{ data.id }}" value="{{ data.value }}" />

                    <ul class="sortable" id="{{ data.id }}_list">

                        <li id="{{ data.id }}_first">
                            <table>
                                <tr>
                                    <td style="vertical-align: top;">
                                        <input type="text" class="regular-text r_title" placeholder="<?php  echo __('Title', 'xapt') ?>" onkeyup="updateColumnData('{{ data.id }}')" /><br />
                                        <textarea class="shortcake-richtext regular-text r_text_content" style="margin: 10px 0 0; width: 300px; height: 131px;" placeholder="<?php  echo __('Content', 'xapt') ?>" onkeyup="updateColumnData('{{ data.id }}')"></textarea>
                                    </td>
                                    <td class="imagecontainer imagedata" style="vertical-align: top; padding-left: 10px;">
                                        <input type="hidden" id="{{ data.id }}_img" class="r_imageid" />
                                        <input type="hidden" id="{{ data.id }}_imgurl" class="r_imageurl" />
                                        <input type="button" class="button-secondary" value="<?php  echo __('Attach Image', 'xapt') ?>" onclick="attachColumnImage('{{ data.id }}',this)" />
                                        <input type="button" class="button-secondary removeimage" style="display: none;" value="<?php  echo __('Remove', 'xapt') ?>" id="{{ data.id }}_removeimage" onclick="removeColumnImage('{{ data.id }}',this)" />
                                        <div style="margin-top: 15px; width: 128px; height: 128px; border: 1px dashed #ccc; background-size: contain; background-repeat: no-repeat; background-position: center;" id="{{ data.id }}_preview" class="previewimage"></div>
                                    </td>
                                </tr>
                            </table>

                            <input type="button" class="button-primary remove-button" value="<?php  echo __('Remove', 'xapt') ?>" onclick="removeColumn('{{ data.id }}', this)" />
                            <hr />
                        </li>

                    </ul>
                </form>
                <script type="text/javascript">initColumns('{{ data.id }}');</script>
            </div>
        </script>

        <?php
    }

}

$feautres_field = Shortcake_Field_Columns::get_instance();

and use it in the shortcode like this:

shortcode_ui_register_for_shortcode( 'columns_block', array(
                'label' => __('Columns Block', 'xapt'),
                'listItemImage' => 'dashicons-layout',
                'post_type'     => array( 'post' ),
                'attrs' => array(
                    array(
                        'label'       => __('Columns Block', 'xapt'),
                        'attr'        => 'columns',
                        'type'        => 'columns',
                    ),
                )
            )
        );
add_shortcode( 'columns_block', function( $attr, $content = '' ) {
            $attr = wp_parse_args( $attr, array(
                'columns' => ''
            ) );

            ob_start();

            if(!empty($attr['columns'])){
                $attr['columns'] = '['.str_replace("'",'"',$attr['columns']).']';
                $columns = json_decode($attr['columns'],true);
                $columnsFull = array();

                foreach($columns as $column){
                    $column['title'] = urldecode($column['title']);
                    $column['text_content'] = urldecode($column['text_content']);
                    $image = wp_get_attachment_image_src( $column['imgid'], 'gallery_thumbnail');
                    $column['imgurl'] = (!empty($image) ? $image[0] : false );
                    $columnsFull[] = $column;
                }

                twig_render('components/shortcodes/shortcode.columns-block.twig', array(
                    'columns' => $columnsFull
                ));

            }else{
                echo '<div style="padding: 10px; width: 100%; text-align: center; border: 1px dotted #ccc;">' . __('Click here to customize', 'xapt') . '</div>';
            }
            return ob_get_clean();

        } );

If you don't use twig just repalce the twig_render function with the output html

mattwatsoncodes commented 7 years ago

Hi @kaoquan did you manage to fix that onkeyup bug? I'm implementing a similar solution, and have the same issue!

mattwatsoncodes commented 7 years ago

@kaoquan I managed a hacky solution, by triggering a change() on another field.

kaoquan commented 7 years ago

@mwtsn Yes I did here it goes

i have a js file for the admin like this

var richTextSelector = 'textarea.shortcake-richtext-field';
var richText = {};

/* globals jQuery, alert */
jQuery(function( $ ) {
    'use strict';

    richText.load = function( selector ) {
        if ( ( 'undefined' !== tinyMCE ) && ( $( selector ).length ) ) {
            $( selector ).each( function() {

                var textarea_id = $(this).attr('id');

                if( null === tinyMCE.get( textarea_id ) ) {

                    // Add a slight delay to offset the loading of any elements on the page. Sometimes doesn't load correctly
                    setTimeout(function () {
                        // Bind tinyMCE to this field
                        tinyMCE.execCommand('mceAddEditor', false, textarea_id );
                    }, 10);

                }

            });

            return true;
        } else {
            return false;
        }
    };

    richText.unload = function( selector ) {

        if ( ( $( selector ).length ) ) {
            $( selector ).each( function() {
                var textarea_id = $( this).attr('id');
                if( null != tinyMCE.get( textarea_id ) ) {
                    // Remove tinyMCE from the field
                    tinymce.execCommand( 'mceRemoveEditor', false, textarea_id );
                }

            });

            return true;
        } else {
            return false;
        }
    };

} );

function addItem(id){
    var list = jQuery('#'+id+'_list');
    var first = jQuery('#'+id+'_first');
    richText.unload( jQuery( first ).find( richTextSelector ) );
    var newElement = first.clone();
    richText.load( jQuery( first ).find( richTextSelector ) );

    newElement.attr('id','');

    jQuery('input[type=text]', newElement).val('');
    jQuery('.previewimage', newElement).css('background-image','none');
    jQuery('.removeimage', newElement).hide();
    jQuery('input[type=hidden]', newElement).val('');
    jQuery('input[type=number]', newElement).val('');
    jQuery('textarea', newElement).val('');

    richText.count++;
    jQuery(newElement).find('.r_text_content').attr('id',id + '-text-cotent-' + richText.count);
    richText.load( jQuery( newElement ).find( richTextSelector ) );

    list.append(newElement);
}

function removeItem(id,element){
    var parent = jQuery(element).parent('li');
    if(jQuery('#'+id+'_list > li').length > 1){
        if(parent.attr('id') == id+'_first') {
            parent.next().attr('id', id+'_first');
        }
        richText.unload( jQuery( parent ).find( richTextSelector ) );
        parent.remove();
    }
}

function attachItemImage(id,element){

    var meta_image_frame;

    if ( meta_image_frame ) {
        wp.media.editor.open();
        return;
    }

    meta_image_frame = wp.media.frames.meta_image_frame = wp.media({
        title: 'Select Image',
        button: { text:  'Insert'},
        library: { type: 'image' }
    });

    meta_image_frame.on('select', function(){
        var media_attachment = meta_image_frame.state().get('selection').first().toJSON();
        var parent = jQuery(element).parents('.imagecontainer');
        parent.find('.r_imageid').val(media_attachment.id);
        parent.find('.r_imageurl').val((media_attachment.sizes && media_attachment.sizes.full.url || media_attachment.url));
        parent.find('.previewimage').css('background-image','url(' + (media_attachment.sizes && media_attachment.sizes.full.url || media_attachment.url) + ')');
        parent.find('.removeimage').show();
    });

    meta_image_frame.open()

}

function removeItemImage(id,elem){
    var parent = jQuery(elem).parents('.imagecontainer');
    parent.find('.r_imageid').val('');
    parent.find('.r_imageurl').val('');
    parent.find('.previewimage').css('background-image','none');
    parent.find('.removeimage').hide();
}

/** fps corencting encoding **/
function htmlspecialchars(str) {
    var map = {
        "<": "%3C",
        ">": "%3E",
        "[": "%5B",
        "]": "%5D",
        "\\": "%5C",
        "\"": "%22",
        "'": "%27",
        "+": "%2B",
        "\n": "%0A",
        "\t": "%09"
    };
    if(str == '<p><br></p>'){
        str = '';
    }
    return str.replace(/[<>\[\]\\"'+\n\t]/g, function(m) { return map[m]; });
}
function htmlspecialchars_decode(str) {
    var map = {
        "%3C": "<",
        "%3E": ">",
        "%5B": "[",
        "%5D": "]",
        "%5C": "\\",
        "%22": "\"",
        "%27": "'",
        "%2B": "+",
        "%0A" : "\n",
        "%09" : "\t"
    };
    return str.replace(/(%3C|%3E|%5B|%5D|%5C|%22|%27|%2B|%0A|%09)/g, function(m) { return map[m]; });
}```

and the class I changed it like this i hope it help, (off for the weekend but will reply for any question on monday)

```<?php

/**
 * Primary controller class for Shortcake Columns Field
 */
class Shortcake_Field_Columns {

    /**
     * Shortcake Columns Field controller instance.
     *
     * @access private
     * @var object
     */
    private static $instance;

    /**
     * All registered post fields.
     *
     * @access private
     * @var array
     */
    private $post_fields  = array();

    /**
     * Settings for the Columns Field.
     *
     * @access private
     * @var array
     */
    private $fields = array(
        'columns' => array(
            'template' => 'fusion-shortcake-field-columns',
            'view'     => 'editAttributeField',
        ),
    );

    /**
     * Get instance of Shortcake Columns Field controller.
     *
     * Instantiates object on the fly when not already loaded.
     *
     * @return object
     */
    public static function get_instance() {
        if ( ! isset( self::$instance ) ) {
            self::$instance = new self;
            self::$instance->setup_actions();
        }
        return self::$instance;
    }

    /**
     * Set up actions needed for Columns Field
     */
    private function setup_actions() {
        add_filter( 'shortcode_ui_fields', array( $this, 'filter_shortcode_ui_fields' ) );
        add_action( 'shortcode_ui_loaded_editor', array( $this, 'load_template' ) );
    }

    /**
     * Whether or not the color attribute is present in registered shortcode UI
     *
     * @return bool
     */
    private function columns_attribute_present() {

        foreach ( Shortcode_UI::get_instance()->get_shortcodes() as $shortcode ) {

            if ( empty( $shortcode['attrs'] ) ) {
                continue;
            }

            foreach ( $shortcode['attrs'] as $attribute ) {
                if ( empty( $attribute['type'] ) ) {
                    continue;
                }

                if ( 'columns' === $attribute['type'] ) {
                    return true;
                }
            }
        }

        return false;
    }

    /**
     * Add Color Field settings to Shortcake fields
     *
     * @param array $fields
     * @return array
     */
    public function filter_shortcode_ui_fields( $fields ) {
        return array_merge( $fields, $this->fields );
    }

    /**
     * Output templates used by the Columns field.
     */
    public function load_template() {

        ?>
        <script type="text/javascript">
            function updateColumnData(id){
                var data = [];

                jQuery.each(jQuery('#'+id+"_list > li"), function(index, value){
                    var columnData = {
                        title: htmlspecialchars(jQuery(value).find('.r_title').val()),
                        text_content: htmlspecialchars(jQuery(value).find('.r_text_content').val()),
                        imgid: jQuery(value).find('.imagedata .r_imageid').val(),
                        imgurl: jQuery(value).find('.imagedata .r_imageurl').val()
                    };
                    data.push(columnData);
                });

                var dataText = JSON.stringify(data);
                dataText = dataText.replace('[','').replace(']','').replace(/[\\"']/g, '\'').replace(/\u0000/g, '\'');
                jQuery( '#'+id ).val( dataText ).trigger( 'input' );
            }

            function initColumns(id){
                richText.count = 0;

                var initdata = jQuery('#'+ id +'_initdata').val();
                initdata = '[' + initdata.replace(/[\\']/g, '"') + ']';
                initdata = JSON.parse(initdata);

                var element = jQuery('#'+id+'_first');
                jQuery(element).find('.r_text_content').attr('id',id + '-text-cotent-' + 0);

                if(initdata.length){
                    richText.count = initdata.length;
                    for(var i = 0; i < initdata.length; i++){
                        var eData = initdata[i];
                        var target;
                        if(i == 0){
                            target = element;
                        } else {
                            target = element.clone();
                            jQuery('input[type=text]', target).val('');
                            jQuery('.previewimage', target).css('background-image','none');
                            jQuery('.removeimage', target).hide();
                            jQuery('input[type=hidden]', target).val('');
                            jQuery('input[type=number]', target).val('');
                            jQuery('textarea', target).val('');
                        }

                        if(eData.title){
                            jQuery(target).find('.r_title').val(htmlspecialchars_decode(eData.title));
                        }
                        if(eData.text_content){
                            jQuery(target).find('.r_text_content').val(htmlspecialchars_decode(eData.text_content));
                        }
                        if(eData.imgid){
                            jQuery(target).find('.imagedata .r_imageid').val(eData.imgid);
                            jQuery(target).find('.imagedata .removeimage').show();
                        }
                        if(eData.imgurl){
                            jQuery(target).find('.imagedata .r_imageurl').val(eData.imgurl);
                            jQuery(target).find('.imagedata .previewimage').css('background-image','url(' +eData.imgurl + ')');
                        }
                        jQuery(target).find('.r_text_content').attr('id',id + '-text-cotent-' + i);

                        if(i != 0){
                            var list = jQuery('#'+id+'_list');
                            target.attr('id','');
                            list.append(target);
                        }
                    }
                }

                richText.load( jQuery( richTextSelector ) );
            }

        </script>

        <script type="text/html" id="tmpl-fusion-shortcake-field-columns">
            <div class="field-block shortcode-ui-field-columns shortcode-ui-attribute-{{ data.attr }}">
                <input type="button" class="button-primary" value="<?php  echo __('Add Column', 'xapt') ?>" id="{{ data.id }}_add_review" onclick="addItem('{{ data.id }}')" />

                <hr />

                <form id="{{ data.id }}_reviews" name="{{ data.id }}_counters">

                    <input type="hidden" class="large-text" name="initdata" id="{{ data.id }}_initdata" value="{{ data.value }}" />

                    <input type="hidden" class="large-text" name="{{ data.attr }}" id="{{ data.id }}" value="{{ data.value }}" />

                    <ul class="sortable" id="{{ data.id }}_list">

                        <li id="{{ data.id }}_first">
                            <table>
                                <tr>
                                    <td style="vertical-align: top;">
                                        <input type="text" class="regular-text r_title" placeholder="<?php  echo __('Title', 'xapt') ?>" /><br />
                                        <textarea class="shortcake-richtext-field regular-text r_text_content" style="margin: 10px 0 0; width: 300px; height: 131px;" placeholder="<?php  echo __('Content', 'xapt') ?>"></textarea><br/>

                                    </td>
                                    <td class="imagecontainer imagedata" style="vertical-align: top; padding-left: 10px;">
                                        <input type="hidden" id="{{ data.id }}_img" class="r_imageid" />
                                        <input type="hidden" id="{{ data.id }}_imgurl" class="r_imageurl" />
                                        <input type="button" class="button-secondary" value="<?php  echo __('Attach Image', 'xapt') ?>" onclick="attachItemImage('{{ data.id }}',this)" />
                                        <input type="button" class="button-secondary removeimage" style="display: none;" value="<?php  echo __('Remove', 'xapt') ?>" id="{{ data.id }}_removeimage" onclick="removeItemImage('{{ data.id }}',this)" />
                                        <div style="margin-top: 15px; width: 128px; height: 128px; border: 1px dashed #ccc; background-size: contain; background-repeat: no-repeat; background-position: center;" id="{{ data.id }}_preview" class="previewimage"></div>
                                    </td>
                                </tr>
                            </table>

                            <input type="button" class="button-primary remove-button" value="<?php  echo __('Remove', 'xapt') ?>" onclick="removeItem('{{ data.id }}', this)" />
                            <hr />
                        </li>

                    </ul>
                </form>
                <script type="text/javascript">
                    initColumns('{{ data.id }}');
                    wp.shortcake.hooks.addAction( 'shortcode-ui.render_destroy', function() {
                        richText.unload( richTextSelector );
                        updateColumnData('{{ data.id }}');
                    } );
                </script>
            </div>
        </script>

        <?php
    }

}

$feautres_field = Shortcake_Field_Columns::get_instance();
kaoquan commented 7 years ago

@mwtsn this is the key wp.shortcake.hooks.addAction( 'shortcode-ui.render_destroy', function() { richText.unload( richTextSelector ); updateColumnData('{{ data.id }}'); } ); it only updateds when you close the popup window. so only one update and not on every key :)

mattwatsoncodes commented 7 years ago

Thank you very much @kaoquan this worked a treat :)