bitovi/canjs

View on GitHub
demos/can-component/accordion.html

Summary

Maintainability
Test Coverage
<div id="out"></div>

<script src="../../node_modules/steal/steal.js" dev-bundle main="@empty">
import { Component, stache, DefineMap, DefineList, viewModel} from "can";

const demoHTML = `<my-accordion>
    <my-panel makeActive:from="makeActive" title:from="'Contacts'">
        <button>Change title attribute to "Users"</button>
        <ul>
            <li>Justin</li>
            <li>Brian</li>
        </ul>
    </my-panel>
    <my-panel makeActive:from="makeActive" title:from="'Libraries'">
        <ul>
            <li>CanJS</li>
            <li>JavaScriptMVC</li>
        </ul>
    </my-panel>
</my-accordion>`;

const Panel = DefineMap.extend({
    active: "boolean",
    title: "string"
});

const AccordionViewModel = DefineMap.extend({
    active: Panel,
    // Contains a list of all panel scopes within the
    // tabs element.
    panels: {
        Default: DefineList
    },
    // When a `<panel>` element is inserted into the document,
    // it calls this method to add the panel's scope to the
    // panels array.
    addPanel: function(panel){
        // If this is the first panel, activate it.
        if( this.panels.length === 0 ) {
            this.makeActive(panel)
        }
        this.panels.push(panel);
    },
    // When a `<panel>` element is removed from the document,
    // it calls this method to remove the panel's scope from
    // the panels array.
    removePanel: function(panel){
        var panels = this.panels;

        panels.splice(panels.indexOf(panel),1);
        // if the panel was active, make the first item active
        if(panel === this.active){
            if(panels.length){
                this.makeActive(panels[0]);
            } else {
                this.active = undefined;
            }
        }
    },
    makeActive: function(panel){
        this.active = panel;
        this.panels.forEach(function(panel){
            panel.active = false;
        });
        panel.active = true;

    }
});

Component.extend({
    tag: "my-accordion",
    ViewModel: AccordionViewModel,
    leakScope: true
});

Component.extend({
    tag:"my-panel",
    view: `
        <h2 on:click='makeActive(this)'>{{ title }}</h2>
        {{# if(active) }}<content></content>{{/ if }}`,
    ViewModel: DefineMap.extend('MyPanelVM', {
        active: {
            type: "boolean",
            default: false
        },
        title: "string",
        connectedCallback: function(element) {
            var panel = this;
            var parentViewModel = element.parentNode.viewModel;
            parentViewModel.addPanel(panel);
            return function() {
                parentViewModel.removePanel(panel);
            };
        }
    })
});

var out = document.getElementById("out");
var template = stache(demoHTML);
out.appendChild(template({}));

out.addEventListener("click", function(ev){
    var el = ev.target;
    var parent = el.parentNode.viewModel;
    if (el.nodeName === "BUTTON") {
        parent.title = "Users";
    }
});

window.canViewModel = viewModel;
</script>