JSFiddle - React, Tailwind, and code Playground

HTML

<script src="http://code.jquery.com/jquery-1.10.1.min.js"></script>
<link rel="stylesheet" href="http://www.cssreset.com/downloads/css-resets/html5-doctor-reset-stylesheet/html5-doctor-reset-stylesheet.min.css">
<!-- ページャ View 再利用可能なので WAF での部品化オススメ -->
<ul class="ko-pager" data-bind="with: pager">
    <li class="btn btn-small" data-bind="click: goToFirst"> 最初 </li>
    <li class="btn btn-small" data-bind="click: goToPrev"> 前ページ </li>
    <li class="pager-input" onclick="$(this).find('input').focus();">
        <form data-bind="submit: goToInputted">
            <input type="text" onfocus="this.select();"
                   data-bind="value: current_input,
                          valueUpdate: 'afterkeydown'" />
            /<span data-bind="text: pages"> </span>
        </form>
    </li>
    <li class="btn btn-small" data-bind="click: goToNext"> 次ページ </li>
    <li class="btn btn-small" data-bind="click: goToLast"> 最後 </li>
    <li class="pager-summary" data-bind="visible: !isLoading()">
        <!--ko if: count() > 0 -->
        <span data-bind="text: count"> </span> 件中
        <span data-bind="text: offset() + 1"> </span> ~
        <span data-bind="text: offset() + items().length"> </span> 件目を表示中
        <!--/ko-->
        <!--ko if: count() == 0 -->
        アイテムが登録されていません
        <!--/ko-->
    </li>
    <li class="indicator" data-bind="visible: isLoading">
        <!-- 実際はローディング画像を入れる -->
        now loading ...
    </li>
</ul>

<!-- リスト View -->
<table>
    <thead>
        <tr>
            <th>ID</th>
            <th>field_a</th>
            <th>field_b</th>
        </tr>
    </thead>
    <tbody data-bind="foreach: entries">
        <tr>
            <td data-bind="text: id"> </td>
            <td data-bind="text: field_a"></td>
            <td data-bind="text: field_b"></td>
        </tr>
    </tbody>
</table>

CSS

ul.ko-pager {
    background-color: #ABE2EA;
    width: 500px;
    height: 24px; }
    ul.ko-pager > li {
      display: inline-block;
      font-size: 14px;
      padding: 0 4px 0 4px; }
      ul.ko-pager > li.pager-input {
        font-size: 13px;
        margin: 4px 0;
        color: #777;
        border: solid 1px #aaa;
        background-color: white;
        padding: 0 2px;
        box-shadow: inset 0 0 3px rgba(0, 0, 0, 0.3);
        border-radius: 3px; }
        ul.ko-pager > li.pager-input input {
          width: 35px;
          text-align: right;
          background-color: transparent;
          border: 0;
          margin: 0;
          padding: 0 2px;
          vertical-align: baseline; }
      ul.ko-pager > li.pager-summary {
        color: #FFF;
        text-shadow: 1px 1px 1px #000 }
      ul.ko-pager > li.btn {
        margin-top: -2px;
        cursor: pointer; }
        ul.ko-pager > li.btn:hover {
          background-color: #bbb; }
table {
  width: 500px; }
  table td, table th {
    padding: 4px;
    border: solid 1px #ccc; }

TypeScript

/**
 * ページャ ViewModel
 * 再利用可能モジュール
 *
 * @param {ko.observableArray} observableItems アイテムを格納するリスト
 * @param {number} [itemsLimit] ページ毎の最大アイテム数
 * @constructor
 */
function PagerViewModel(observableItems, itemsLimit) {
    var self = this;

    self.isLoading = ko.observable(false);        // 読み込み中か否か
    self.count = ko.observable(0);                // アイテム総数
    self.current = ko.observable(1);              // 現在のページ番号
    self.current_input = ko.observable(1);        // 現在のページ番号(入力値)
    self.limit = ko.observable( itemsLimit || 20 ); // 1ページ中の最大アイテム数
    self.pages = ko.computed(function() {         // 総ページ数
        return Math.ceil(self.count() / self.limit())
    });
    self.offset = ko.computed(function() {        // 表示中のアイテムオフセット
        return (self.current() - 1) * self.limit()
    });
    self.items = observableItems;                 // アイテムリスト

    // ページ移動アクション
    self.goTo = function(page) {
        if (page < 1) page = 1;
        if (page > self.pages()) page = self.pages();
        self.current(page);
        self.current_input(page);
    };
    self.goToFirst = function() { self.goTo(1) };
    self.goToPrev = function() { self.goTo(self.current() - 1) };
    self.goToNext = function() { self.goTo(self.current() + 1) };
    self.goToLast = function() { self.goTo(self.pages()) };
    self.goToInputted = function() { self.goTo(self.current_input()) };
}

/**
 * アプリケーション ViewModel
 * @constructor
 */
function AppViewModel() {
    var self = this;
    
    // fields
    self.api = new DummyApi();
    self.entries = ko.observableArray([]);
    self.pager = new PagerViewModel(self.entries, 15);
    
    // methods
    self.reload = function() {
        self.pager.isLoading(true);
        self.api
            .getEntries( self.pager.offset(), self.pager.limit() )
            .then(function(response) {
                if (response.result != 'success') {
                    alert(response.error);
                    return;
                }
            ...