JSFiddle - React, Tailwind, and code Playground

by manchagnu

HTML

<script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
<!-- ko paginate: true -->
    <button data-bind="enable: hasPrevPage, click:prevPage">Prev</button>
    <div id="nav" data-bind="text:'Page ' + currentPage() + ' of ' + numPages()"></div>
    <button data-bind="enable: hasNextPage, click: nextPage">Next</button>
    <div class="pages">
        <div class="active">
            <input data-bind="value: firstName"/>
            <input data-bind="value: lastName"/>
        </div>
        <div>
            <b>Blank Page 1</b>
        </div>
        <div>
            <input data-bind="value: email"/>
            <input data-bind="value: phone"/>
        </div>
        <div>
            <b>Blank Page 2</b>
        </div>
        <div>
            <b>Blank Page 3</b>
        </div>
    </div>

    <button data-bind="click:function(){ console.log(ko.toJSON($root)); }">Output</button>
<!-- /ko -->

CSS

body {
    font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;
    text-align:center;
}

button {
    font-family:inherit;
}

.pages > div {
    display:none;
    width:300px;
    height:300px;
    border:1px solid gray;
    padding:20px;
    margin:auto;
}

.pages > div.active {
    display:block;
}

#nav {
    display:inline;
}

JavaScript

ko.bindingHandlers.paginate = {
    init: function(elem, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
        var pages = []
            , ctnr = ko.virtualElements.firstChild(elem)
            , pagesElem
            , currentPage = ko.observable(1)
            , model = {
                currentPage: ko.computed({
                    read: currentPage
                    , write: function(value) {
                        var prev = currentPage();
                        currentPage(value);
                        pages.removeClass('active').eq(value - 1).addClass('active');
                    }
                })
                , numPages: ko.observable(0)
                , nextPage: function() { model.currentPage(currentPage() + 1); }
                , prevPage: function() { model.currentPage(currentPage() - 1); }
            }
        ;

        while (ctnr) {
            pagesElem = $(ctnr);
            if (pagesElem.hasClass('pages')) {
                pages = $(pagesElem).children();
                model.numPages(pages.length);
                break;
            }
            ctnr = ko.virtualElements.nextSibling(ctnr);
        }
        model.hasPrevPage = ko.computed(function() { return currentPage() > 1; });
        model.hasNextPage = ko.computed(function() { return currentPage() < model.numPages(); });
        ko.applyBindingsToDescendants(bindingContext.extend(model), elem);
        return { controlsDescendantBindings: true };
    }
};
ko.virtualElements.allowedBindings.paginate = true;

ko.applyBindings({
    firstName: ko.observable("John"),
    lastName: ko.observable("Smith"),
    email: ko.observable("[email protected]"),
    phone: ko.observable("555-555-5555")
});