AEM 63 - Touch UI Expandable Collapsible Composite Multifield

Goal


Extend the product Composite Multifield to provide Expand / Collapse feature, for better experience and easy reordering

Demo | Package Install | Github


All Items Collapsed



One Item Expanded

Easy Reordering

Solution


1) Login to CRXDE Lite (http://localhost:4502/crx/de), create folder /apps/eaem-touchui-collapsible-multifield

2) Sample dialog with composite multifield /apps/eaem-touchui-collapsible-multifield/sample-collapsible-multifield/cq:dialog
<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/1.0" xmlns:cq="http://www.day.com/jcr/cq/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:nt="http://www.jcp.org/jcr/nt/1.0"
    jcr:primaryType="nt:unstructured"
    jcr:title="63 Collapsible Multifield"
    sling:resourceType="cq/gui/components/authoring/dialog">
    <content
        jcr:primaryType="nt:unstructured"
        sling:resourceType="granite/ui/components/coral/foundation/fixedcolumns">
        <items jcr:primaryType="nt:unstructured">
            <column
                jcr:primaryType="nt:unstructured"
                sling:resourceType="granite/ui/components/coral/foundation/container">
                <items jcr:primaryType="nt:unstructured">
                    <products
                        jcr:primaryType="nt:unstructured"
                        sling:resourceType="granite/ui/components/coral/foundation/form/multifield"
                        composite="{Boolean}true"
                        eaem-show-on-collapse="EAEM.showProductName"
                        fieldLabel="Products">
                        <field
                            jcr:primaryType="nt:unstructured"
                            sling:resourceType="granite/ui/components/coral/foundation/container"
                            name="./products">
                            <items jcr:primaryType="nt:unstructured">
                                <column
                                    jcr:primaryType="nt:unstructured"
                                    sling:resourceType="granite/ui/components/coral/foundation/container">
                                    <items jcr:primaryType="nt:unstructured">
                                        <product
                                            jcr:primaryType="nt:unstructured"
                                            sling:resourceType="granite/ui/components/coral/foundation/form/textfield"
                                            fieldDescription="Name of Product"
                                            fieldLabel="Product Name"
                                            name="./product"/>
                                        <path
                                            jcr:primaryType="nt:unstructured"
                                            sling:resourceType="granite/ui/components/coral/foundation/form/pathbrowser"
                                            fieldDescription="Select Path"
                                            fieldLabel="Path"
                                            name="./path"
                                            rootPath="/content"/>
                                        <startDate
                                            jcr:primaryType="nt:unstructured"
                                            sling:resourceType="granite/ui/components/coral/foundation/form/datepicker"
                                            class="field"
                                            displayedFormat="YYYY-MM-DD HH:mm"
                                            fieldLabel="Start Date"
                                            name="./startDate"
                                            type="datetime"/>
                                        <show
                                            jcr:primaryType="nt:unstructured"
                                            sling:resourceType="granite/ui/components/coral/foundation/form/checkbox"
                                            name="./show"
                                            text="Show?"
                                            value="yes"/>
                                        <type
                                            jcr:primaryType="nt:unstructured"
                                            sling:resourceType="granite/ui/components/coral/foundation/form/select"
                                            fieldDescription="Select Size"
                                            fieldLabel="Size"
                                            name="./size">
                                            <items jcr:primaryType="nt:unstructured">
                                                <def
                                                    jcr:primaryType="nt:unstructured"
                                                    text="Select Size"
                                                    value=""/>
                                                <small
                                                    jcr:primaryType="nt:unstructured"
                                                    text="Small"
                                                    value="small"/>
                                                <medium
                                                    jcr:primaryType="nt:unstructured"
                                                    text="Medium"
                                                    value="medium"/>
                                                <large
                                                    jcr:primaryType="nt:unstructured"
                                                    text="Large"
                                                    value="large"/>
                                            </items>
                                        </type>
                                    </items>
                                </column>
                            </items>
                        </field>
                    </products>
                </items>
            </column>
        </items>
    </content>
</jcr:root>


3) #14 eaem-show-on-collapse property is for the function creating summary of multifield items in collapsed mode; here it is EAEM.showProductName, returns the first field value in multifield item (if empty or error in creating summary Click to expand text is shown)

4) Create node /apps/eaem-touchui-collapsible-multifield/clientlib of type cq:ClientLibraryFolder, add String property categories with value cq.authoring.dialog.all, String property dependencies with value underscore

5) Create file (nt:file) /apps/eaem-touchui-collapsible-multifield/clientlib/js.txt, add

                       collapsible-multifield.js

6) Create file (nt:file) /apps/eaem-touchui-collapsible-multifield/clientlib/collapsible-multifield.js, add the following code

(function(){
    if(typeof EAEM === "undefined"){
        EAEM = {};
    }

    //the function executed when user clicks collapse; returns summary of multifield item data
    EAEM.showProductName = function(fields){
        return Object.values(fields)[0];
    }
}());

(function ($, $document, gAuthor) {
    var summaryCreators = {},
        CORAL_MULTIFIELD = "coral-multifield",
        CORAL_MULTIFIELD_ITEM = "coral-multifield-item",
        CORAL_MULTIFIELD_ITEM_CONTENT = "coral-multifield-item-content",
        EAEM_SHOW_ON_COLLAPSE = "eaem-show-on-collapse",
        RS_MULTIFIELD = "granite/ui/components/coral/foundation/form/multifield";

    $document.on("dialog-ready", addCollapsers);

    function addCollapsers(){
        var $multifields = $(CORAL_MULTIFIELD).css("padding-right", "2.5rem");

        if(_.isEmpty($multifields)){
            return;
        }

        $multifields.find(CORAL_MULTIFIELD_ITEM).each(handler);

        $multifields.on('change', function(){
            $multifields.find(CORAL_MULTIFIELD_ITEM).each(handler);
        });

        loadShowSummaryCreatorFunctions();

        addExpandCollapseAll($multifields);

        function handler(){
            var $mfItem = $(this);

            if(!_.isEmpty($mfItem.find("[icon=accordionUp]"))){
                return;
            }

            addAccordionIcons($mfItem);

            addSummarySection($mfItem);
        }
    }

    function addSummarySection($mfItem){
        var $summarySection = $("<div/>").insertAfter($mfItem.find(CORAL_MULTIFIELD_ITEM_CONTENT))
            .addClass("coral-Well").css("cursor", "pointer").hide();

        $summarySection.click(function(){
            $mfItem.find("[icon='accordionDown']").click();
        });
    }

    function addExpandCollapseAll($multifields){
        var $mfAdd, expandAll, collapseAll;

        $multifields.find("[coral-multifield-add]").each(handler);

        function handler(){
            $mfAdd = $(this);

            expandAll = new Coral.Button().set({
                variant: 'secondary',
                innerText: "Expand All"
            });

            expandAll.$.css("margin-left", "10px").click(function(){
                event.preventDefault();
                $(this).closest(CORAL_MULTIFIELD).find("[icon='accordionDown']").click();
            });

            collapseAll = new Coral.Button().set({
                variant: 'secondary',
                innerText: "Collapse All"
            });

            collapseAll.$.css("margin-left", "10px").click(function(){
                event.preventDefault();
                $(this).closest(CORAL_MULTIFIELD).find("[icon='accordionUp']").click();
            });

            $mfAdd.after(expandAll).after(collapseAll);
        }
    }

    function loadShowSummaryCreatorFunctions(){
        var editable = gAuthor.DialogFrame.currentDialog.editable;

        if(!editable){
            return;
        }

        $.ajax(editable.config.dialog + ".infinity.json").done(fillLabelCreatorFns);

        function fillLabelCreatorFns(obj){
            if(!_.isObject(obj) || _.isEmpty(obj)){
                return;
            }

            _.each(obj, function(value){
                if(value["sling:resourceType"] === RS_MULTIFIELD){
                    if(!_.isEmpty(value.field) && !_.isEmpty(value.field.name)) {
                        summaryCreators[value.field.name] = value[EAEM_SHOW_ON_COLLAPSE];
                    }
                }else{
                    if(_.isObject(value) && !_.isEmpty(value)){
                        fillLabelCreatorFns(value);
                    }
                }
            });
        }
    }

    function addAccordionIcons($mfItem){
        var up = new Coral.Button().set({
            variant: "quiet",
            icon: "accordionUp",
            title: "Collapse"
        });

        up.setAttribute('style', 'position:absolute; top: 0; right: -2.175rem');
        up.$.on('click', handler);

        $mfItem.append(up);

        var down = new Coral.Button().set({
            variant: "quiet",
            icon: "accordionDown",
            title: "Expand"
        });

        down.setAttribute('style', 'position:absolute; top: 0; right: -2.175rem');
        down.$.on('click', handler).hide();

        $mfItem.append(down);

        function handler(event){
            event.preventDefault();

            var mfName = $(this).closest(CORAL_MULTIFIELD).attr("data-granite-coral-multifield-name"),
                $mfItem = $(this).closest(CORAL_MULTIFIELD_ITEM),
                $summarySection = $mfItem.children("div");

            $summarySection.html(getSummary($mfItem, mfName));

            adjustUI.call(this, $summarySection);
        }

        function adjustUI($summarySection){
            var icon = $(this).find("coral-icon").attr("icon"),
                $content = $mfItem.find(CORAL_MULTIFIELD_ITEM_CONTENT);

            if(icon == "accordionUp"){
                if($summarySection.css("display") !== "none"){
                    return;
                }

                $summarySection.show();

                $content.slideToggle( "fast", function() {
                    $content.hide();
                });

                up.$.hide();
                down.$.show();
            }else{
                if($summarySection.css("display") === "none"){
                    return;
                }

                $summarySection.hide();

                $content.slideToggle( "fast", function() {
                    $content.show();
                });

                up.$.show();
                down.$.hide();
            }
        }
    }

    function getSummary($mfItem, mfName){
        var summary = "Click to expand";

        try{
            if(summaryCreators[mfName]){
                var fields = {};

                $mfItem.find("input").each(function(){
                    var $input = $(this);
                    fields[$input.attr("name")] = $input.val();
                });

                summary = eval(summaryCreators[mfName])(fields);
            }
        }catch(err){}

        if(!summary){
            summary = "Click to expand";
        }

        return summary;
    }
}(jQuery, jQuery(document), Granite.author));

No comments:

Post a Comment