bitovi/canjs

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

Summary

Maintainability
Test Coverage
<style type="text/css">
body {font-family: verdana}
my-tabs {
    margin-top: 20px;
}
button {clear: both;}
my-tabs ul {
    padding: 0px; margin: 0px;
}
my-tabs li {
    float: left;
    padding: 10px;
    background-color: #F6F6F6;
    list-style: none;
    margin-left: 10px;
}
my-tabs li {
    color: #1C94C4;
    font-weight: bold;
    text-decoration: none;
}
my-tabs li.active {
    color: #F6A828;
    cursor: default;
}
my-panel {
    clear: both;
    display: block;
}
/* clearfix from jQueryUI */
my-tabs ul:after  { content: "."; display: block; height: 1px; clear: both; visibility: hidden; }
my-tabs ul { display: inline-block; }
</style>
<my-app id="out"></my-app>
<script src="../../node_modules/steal/steal.js" dev-bundle main="@empty" id="demo-source">
import { Component, queues } from "can";


Component.extend({
    tag: "my-tabs",
    view:`
        <ul>
            {{# for(panel of this.panels) }}
                <li {{# this.isActive(panel) }}class='active'{{/ }}
                    on:click='this.makeActive(panel)'>
                    {{ panel.title }}
                </li>
            {{/ for }}
        </ul>
        <content></content>`,
    ViewModel: {
        // Contains a list of all panel scopes within the
        // tabs element.
        panels: {
            default: () => []
        },
        active: "any",

        // When a `<my-panel>` element is inserted into the document,
        // it calls this method to add the panel's viewModel 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 `<my-panel>` element is removed from the document,
        // it calls this method to remove the panel's viewModel from
        // the panels array.
        removePanel: function(panel){
            const panels = this.panels;
            queues.batch.start();
            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;
                }
            }
            queues.batch.stop()
        },
        makeActive: function(panel){
        this.active = panel;
            this.panels.forEach(function(panel){
                panel.active = false;
            });
            panel.active = true;

        },
        // this is viewModel, not mustache
        // consider removing viewModel as arg
        isActive: function( panel ) {
            return this.active === panel;
        }
    }
});

Component.extend({
    view: "{{# if(this.active) }}<content></content>{{/ if }}",
    tag:"my-panel",
    ViewModel: {
        active: {
            type: "boolean",
            default: false
        },
        connectedCallback: function(element) {
            const panel = this;
            const parentViewModel = element.parentNode.viewModel;
            parentViewModel.addPanel(panel);
            return () => {
                this.stopListening();
                parentViewModel.removePanel(panel);
            };
        }
    }
});


Component.extend({
    tag: "my-app",
    view: `
        <p>
            <button on:click="this.addVegies()">Add Vegetables</button>
        </p>
        <my-tabs>
            {{# for(foodType of this.foodTypes) }}
                <my-panel title:from='foodType.title'>
                    {{ foodType.content }}
                </my-panel>
            {{/ for }}
        </my-tabs>
    `,
    ViewModel: {
        foodTypes: {
            default(){
                return [
                    {title: "Fruits", content: "oranges, apples"},
                    {title: "Breads", content: "pasta, cereal"},
                    {title: "Sweets", content: "ice cream, candy"}
                ];
            }
        },
        addVegies(){
            this.foodTypes.push({
                title: "Vegetables",
                content: "Carrots, peas, kale"
            });
        }
    }
});
</script>