JSFiddle - React, Tailwind, and code Playground

by arhari

HTML

<link rel="stylesheet" href="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.11/themes/blitzer/jquery-ui.css">
<script src="https://github.com/jquery/jquery-tmpl/raw/master/jquery.tmpl.js"></script>
<script src="https://github.com/SteveSanderson/knockout/raw/master/build/output/knockout-latest.debug.js"></script>
<table>
    <tr>
        <th>Id</th>
        <th>Name</th>
        <th></th>
        <th></th>
    </tr>
    <tbody data-bind="foreach: productList">
        <tr>
            <td data-bind="text: id"></td>
            <td data-bind="text: name"></td> 
            <td>
                <button data-bind="click: $root.editProduct">Edit</button>
            </td>
            <td>
                <button data-bind="click: $root.removeProduct">Delete</button>
            </td>
        </tr>    
    </tbody>
</table>

<button data-bind="click: addProduct">Add Product</button>


<hr />

<div id="details" data-bind="jqDialog: { autoOpen: false, resizable: false, modal: true }, template: { name: 'editTmpl', data: selectedProduct }, openDialog: selectedProduct, onCloseDialog: onCloseDialog"> 
</div>

<script id="editTmpl" type="text/html">
    <p>
        <label>ID: </label>
        <input data-bind="value: editId"  />
    </p>
    <p>
        <label>Name: </label>
        <input data-bind="value: editName"  />
    </p>        
    <button data-bind="jqButton: {}, click: $root.accept">Accept</button>
    <button data-bind="jqButton: {}, click: $root.cancel">Cancel</button>
</script>

CSS

td { padding: 5px; }
input { width: 100px; margin: 10px;  }
label: { width: 100px; }

JavaScript

//custom binding to initialize a jQuery UI dialog
ko.bindingHandlers.jqDialog = {
    init: function(element, valueAccessor) {
        var options = ko.utils.unwrapObservable(valueAccessor()) || {};
        
        //handle disposal
        ko.utils.domNodeDisposal.addDisposeCallback(element, function() {
            $(element).dialog("destroy");
        }); 
        
        $(element).dialog(options);  
    }
};

ko.bindingHandlers.onCloseDialog = {
    init: function(element, valueAccessor) {
        var listener = ko.utils.unwrapObservable(valueAccessor());
        $(element).dialog({ close: listener });
    }
};

//custom binding handler that opens/closes the dialog
ko.bindingHandlers.openDialog = {
    update: function(element, valueAccessor) {
        var value = ko.utils.unwrapObservable(valueAccessor());
        if (value) {
            $(element).dialog("open");
        } else {
            $(element).dialog("close");
        }
    }
}

//custom binding to initialize a jQuery UI button
ko.bindingHandlers.jqButton = {
    init: function(element, valueAccessor) {
        var options = ko.utils.unwrapObservable(valueAccessor()) || {};
        
        //handle disposal
        ko.utils.domNodeDisposal.addDisposeCallback(element, function() {
            $(element).button("destroy");
        }); 
        
        $(element).button(options);  
    }    
};



var Product = function(id, name, isNew) {
    this.id = ko.observable(id);
    this.name = ko.observable(name);
    this.isNew = isNew;
    this.editId = ko.observable(id);
    this.editName = ko.observable(name);

    //persist edits to real values on accept
    this.accept = function() {
        this.id(this.editId()).name(this.editName());
    }.bind(this);

    //reset to originals on cancel
    this.cancel = function() {
        this.editId(this.id()).editName(this.name());
    }.bind(this);
}

var ViewModel = function() {
    var self = this;
    this.productList = ko.observableArray([
       ...