Search code examples
javascripttabulator

Tabulator 4.2 - how to use a multi element cell for editing


I'm trying to edit a cell with an input and a dropdown. The goal being to edit, in this case, a disk size.

I would therefore need to combine , editor:"input", editor:true, validator:["min:0", "max:999", "numeric"]} with , editor:"select", editorParams:{"MB":"MB", "TB":"TB", "GB":"GB"}

The final result being something like 146GB.

1st try: Since this looks like a custom editor, I wrote this:

var CapacityEditor = function(cell, onRendered, success, cancel, editorParams){
    var capacity = document.createElement("div");
    capacity.style.width = "100%";

    var size = document.createElement("input");
    size.setAttribute("type", "text");
    size.style.width = "50px";
    size.style.textAlign = "right";
    onRendered(function(){
        size.focus();
        //size.style.css = "100%";
    });
    capacity.append(size);

    var multiplier = document.createElement("select");
    multiplier.style.width = "45px";
    multiplier.append(new Option("MB","MB",true,true));
    multiplier.append(new Option("GB","GB"));
    multiplier.append(new Option("TB","TB"));
    capacity.append(multiplier);
    var multi = "MB";

    function successFunc(){
        success(size.value+multi);
    }

    function setmultiFunc() {
        multi = multiplier.options[multiplier.selectedIndex].text;
        successFunc();
    }

    size.addEventListener("change", successFunc);
    size.addEventListener("blur", successFunc);
    multiplier.addEventListener("change", successFunc);
    multiplier.addEventListener("blur", successFunc);

    return capacity;
}

with:

{title:"Capacity", field:"Size", align:"center", width:95, editor:CapacityEditor, sorter:SizeSorter, sortable: true},

So, on that one, there are 'a few' issues. - I have no idea what the success() is supposed to do - I'm returning a div hoping that the content would populate the cell - Whenever I put in a value and make a selection, it does not get saved at all; probably because I'm returning a div in the first place

2nd try: Since the edit does not work, maybe I could use the cell click event instead...

The cell.getElement() does give me the DIV content for the cell as expected but there is no such thing as a cell.setElement(), right? I can create my own div, like in the first try and then use the cell.setValue() to well, set the value but if I can't display my div in the first place, that doesn't work too good. Tried cell.getElement().innerHTML = "<div>...</div>" but nothing (waaaay too easy)

3rd try: Well, let's thing outside the box then (literally)

In other words, lets create a very simple modal, display it on top of the existing cell and then use, again, cell.setValue() to update the value. Problem is: how do I get the position of the cell? Using the built-in editor, the editor:"select" replaces the cell's div with an input in readonly and created a div in the document body with multiple div and then positions it right below the cell making it look like a dropdown:

<input type="text" readonly="" style="padding: 4px; width: 100%; box-sizing: border-box; height: 100%;">
<div class="tabulator-edit-select-list" style="min-width: 80px; top: 348px; left: 671px;"><div class="tabulator-edit-select-list-item" tabindex="0">10K</div><div class="tabulator-edit-select-list-item" tabindex="0">15K</div><div class="tabulator-edit-select-list-item" tabindex="0">7.2K</div><div class="tabulator-edit-select-list-item active" tabindex="0">na</div></div>

Note the top and left style attributes.

Oli is able to do it so there has to be a way, right?

Any help, advise, fix would be much appreciated.


Solution

  • Ok, so, if anyone is interested, I was able to get the cell's position using a function found on this forum by meouw (https://stackoverflow.com/a/442474/2868497):

    function getOffset( el ) {
        var _x = 0;
        var _y = 0;
        while( el && !isNaN( el.offsetLeft ) && !isNaN( el.offsetTop ) ) {
            _x += el.offsetLeft - el.scrollLeft;
            _y += el.offsetTop - el.scrollTop;
            el = el.offsetParent;
        }
        return { top: _y, left: _x };
    }
    

    With the cell's top and left position, I was able to place my div to edit my field:

    var globalCellClick = function(e, cell){
        var capacity = document.createElement("div");
        capacity.style.position = "absolute";
        capacity.style.left = getOffset(cell.getElement()).left + "px";
        capacity.style.top = getOffset(cell.getElement()).top + "px";
        capacity.setAttribute("name", "capacity-edit");
    
        var size = document.createElement("input");
        size.setAttribute("type", "", "text");
        size.style.width = "47px";
        size.style.height = "31px";
        size.style.textAlign = "right";
        capacity.append(size);
    
        var multiplier = document.createElement("select");
        multiplier.style.width = "47px";
        multiplier.style.height = "31px";
        multiplier.append(new Option("MB","MB",true,true));
        multiplier.append(new Option("GB","GB"));
        multiplier.append(new Option("TB","TB"));
        capacity.append(multiplier);
    
        document.body.appendChild(capacity); 
    
        function updateCell(){
            var multi = multiplier.options[multiplier.selectedIndex].text;
            cell.setValue(size.value+multi);
        }
    
        size.focus();
        size.addEventListener("change", updateCell);
        size.addEventListener("blur", updateCell);
        multiplier.addEventListener("change", updateCell);
        multiplier.addEventListener("blur", updateCell);
    };
    

    with:

    {title:"Capacity", field:"Size", align:"center", width:95, cellClick:globalCellClick, sorter:SizeSorter, sortable: true},
    

    I can edit the rest of the fields as needed and I'm then using a save button to save the modifications and destroy all the div I created to edit:

    $("#tabulator-controls  button[name=save-data]").on("click", function(){
        $("div[name=capacity-edit]").remove();
        // update the database
    });
    

    There may be an easier/cleaner way to get it done but it works so happy with it.