awsmug/torro-forms

View on GitHub
assets/src/js/admin-form-builder/views/container-view.js

Summary

Maintainability
F
2 wks
Test Coverage
( function( torro, $, _ ) {
    'use strict';

    /**
     * A container view.
     *
     * @class
     *
     * @param {torro.Builder.Container} container Container model.
     * @param {object}                  options   View options.
     */
    function ContainerView( container, options ) {
        var id       = container.get( 'id' );
        var selected = container.get( 'id' ) === container.collection.props.get( 'selected' );

        this.container = container;
        this.options = options || {};

        this.tabTemplate = torro.template( 'container-tab' );
        this.panelTemplate = torro.template( 'container-panel' );
        this.footerPanelTemplate = torro.template( 'container-footer-panel' );

        this.$tab = $( '<button />' );
        this.$tab.attr( 'type', 'button' );
        this.$tab.attr( 'id', 'container-tab-' + id );
        this.$tab.addClass( 'torro-form-canvas-tab' );
        this.$tab.attr( 'aria-controls', 'container-panel-' + id + ' container-footer-panel-' + id );
        this.$tab.attr( 'aria-selected', selected ? 'true' : 'false' );
        this.$tab.attr( 'role', 'tab' );

        this.$panel = $( '<div />' );
        this.$panel.attr( 'id', 'container-panel-' + id );
        this.$panel.addClass( 'torro-form-canvas-panel' );
        this.$panel.attr( 'aria-labelledby', 'container-tab-' + id );
        this.$panel.attr( 'aria-hidden', selected ? 'false' : 'true' );
        this.$panel.attr( 'role', 'tabpanel' );

        this.$footerPanel = $( '<div />' );
        this.$footerPanel.attr( 'id', 'container-footer-panel-' + id );
        this.$footerPanel.addClass( 'torro-form-canvas-panel' );
        this.$footerPanel.attr( 'aria-labelledby', 'container-tab-' + id );
        this.$footerPanel.attr( 'aria-hidden', selected ? 'false' : 'true' );
        this.$footerPanel.attr( 'role', 'tabpanel' );

        this.addElementFrame = new torro.Builder.AddElement.View.Frame({
            title: torro.Builder.i18n.selectElementType,
            buttonLabel: torro.Builder.i18n.insertIntoContainer,
            collection: torro.Builder.getInstance().elementTypes
        });
    }

    _.extend( ContainerView.prototype, {
        render: function() {
            var i;

            this.$tab.html( this.tabTemplate( this.container.attributes ) );
            this.$panel.html( this.panelTemplate( this.container.attributes ) );
            this.$footerPanel.html( this.footerPanelTemplate( this.container.attributes ) );

            for ( i = 0; i < this.container.elements.length; i++ ) {
                this.listenAddElement( this.container.elements.at( i ) );
            }

            this.attach();
        },

        destroy: function() {
            this.detach();

            this.$tab.remove();
            this.$panel.remove();
            this.$footerPanel.remove();
        },

        attach: function() {
            this.container.on( 'remove', this.listenRemove, this );
            this.container.elements.on( 'add', this.listenAddElement, this );
            this.container.on( 'change:label', this.listenChangeLabel, this );
            this.container.on( 'change:sort', this.listenChangeSort, this );
            this.container.collection.props.on( 'change:selected', this.listenChangeSelected, this );

            this.addElementFrame.on( 'insert', this.addElement, this );

            this.$tab.on( 'click', _.bind( this.setSelected, this ) );
            this.$tab.on( 'dblclick', _.bind( this.editLabel, this ) );
            this.$panel.on( 'click', '.add-element-toggle', _.bind( this.openAddElementFrame, this ) );
            this.$footerPanel.on( 'click', '.delete-container-button', _.bind( this.deleteContainer, this ) );
            this.$panel.find( '.drag-drop-area' ).sortable({
                handle: '.torro-element-header',
                items: '.torro-element',
                placeholder: 'torro-element-placeholder',
                tolerance: 'pointer',
                start: function( e, ui ) {
                    ui.placeholder.height( ui.item.height() );
                },
                update: _.bind( this.updateElementsSorted, this )
            });
        },

        detach: function() {
            this.$panel.find( 'drag-drop-area' ).sortable( 'destroy' );
            this.$footerPanel.off( 'click', '.delete-container-button', _.bind( this.deleteContainer, this ) );
            this.$panel.off( 'click', '.add-element-toggle', _.bind( this.openAddElementFrame, this ) );
            this.$tab.off( 'dblclick', _.bind( this.editLabel, this ) );
            this.$tab.off( 'click', _.bind( this.setSelected, this ) );

            this.addElementFrame.off( 'insert', this.addElement, this );

            this.container.collection.props.off( 'change:selected', this.listenChangeSelected, this );
            this.container.off( 'change:sort', this.listenChangeSort, this );
            this.container.off( 'change:label', this.listenChangeLabel, this );
            this.container.elements.off( 'add', this.listenAddContainer, this );
            this.container.off( 'remove', this.listenRemove, this );
        },

        listenRemove: function() {
            var id = this.container.get( 'id' );

            if ( ! torro.isTempId( id ) ) {
                $( '#torro-deleted-wrap' ).append( '<input type="hidden" name="' + torro.getDeletedFieldName( this.container ) + '" value="' + id + '" />' );
            }

            this.destroy();

            torro.Builder.getInstance().trigger( 'removeContainer', [ this.container, this ] );
        },

        listenAddElement: function( element ) {
            var view = new torro.Builder.ElementView( element, this.options );
            var $dragDropArea = this.$panel.find( '.drag-drop-area' );

            $dragDropArea.append( view.$wrap );

            view.render();

            if ( $dragDropArea.sortable( 'instance' ) ) {
                $dragDropArea.sortable( 'refresh' );
            }

            torro.Builder.getInstance().trigger( 'addElement', [ element, view ] );
        },

        listenChangeLabel: function( container, label ) {
            var name = torro.escapeSelector( torro.getFieldName( this.container, 'label' ) );

            this.$panel.find( 'input[name="' + name + '"]' ).val( label );
        },

        listenChangeSort: function( container, sort ) {
            var name = torro.escapeSelector( torro.getFieldName( this.container, 'sort' ) );

            this.$panel.find( 'input[name="' + name + '"]' ).val( sort );
        },

        listenChangeSelected: function( props, selected ) {
            if ( selected === this.container.get( 'id' ) ) {
                this.$tab.attr( 'aria-selected', 'true' );
                this.$panel.attr( 'aria-hidden', 'false' );
                this.$footerPanel.attr( 'aria-hidden', 'false' );
            } else {
                this.$tab.attr( 'aria-selected', 'false' );
                this.$panel.attr( 'aria-hidden', 'true' );
                this.$footerPanel.attr( 'aria-hidden', 'true' );
            }
        },

        setSelected: function() {
            this.container.collection.props.set( 'selected', this.container.get( 'id' ) );
        },

        editLabel: function() {
            var container = this.container;
            var $original = this.$tab.find( 'span' );
            var $replacement;

            if ( ! $original.length ) {
                return;
            }

            $replacement = $( '<input />' );
            $replacement.attr( 'type', 'text' );
            $replacement.val( $original.text() );

            $replacement.on( 'keydown blur', function( event ) {
                var proceed = false;
                var value;

                if ( 'keydown' === event.type ) {
                    if ( 13 === event.which ) {
                        proceed = true;

                        event.preventDefault();
                    } else if ( [ 32, 37, 38, 39, 40 ].includes( event.which ) ) {
                        event.stopPropagation();
                    }
                } else if ( 'blur' === event.type ) {
                    proceed = true;
                } else {
                    event.stopPropagation();
                }

                if ( ! proceed ) {
                    return;
                }

                value = $replacement.val();

                container.set( 'label', value );

                $original.text( value );
                $replacement.replaceWith( $original );
                $original.focus();
            });

            $original.replaceWith( $replacement );
            $replacement.focus();
        },

        openAddElementFrame: function() {
            this.addElementFrame.open();
        },

        addElement: function( selectedElementType ) {
            var element;

            if ( ! selectedElementType ) {
                return;
            }

            element = this.container.elements.create({
                type: selectedElementType
            });

            this.container.elements.toggleActive( element.get( 'id' ) );
        },

        deleteContainer: function() {
            torro.askConfirmation( torro.Builder.i18n.confirmDeleteContainer, _.bind( function() {
                this.container.collection.remove( this.container );
            }, this ) );
        },

        updateElementsSorted: function( e, ui ) {
            var container = this.container;

            ui.item.parent().find( '.torro-element' ).each( function( index ) {
                var $element = $( this );
                var element  = container.elements.get( $element.attr( 'id' ).replace( 'torro-element-', '' ) );

                element.set( 'sort', index );
            });

            container.elements.sort();
        }
    });

    torro.Builder.ContainerView = ContainerView;

})( window.torro, window.jQuery, window._ );