frappe / builder

Craft beautiful websites effortlessly with an intuitive visual builder and publish them instantly
https://frappe.io/builder
GNU Affero General Public License v3.0
441 stars 132 forks source link

feat: datatables! #152

Open batonac opened 4 months ago

batonac commented 4 months ago

The smart people over at Frappe built a really cool datatable. 😉

Can we get a block for that in Builder? Ideally with full support for dynamic data. 😀

batonac commented 4 months ago

This is a quite hacky, but I did find a way to force the datatable into operation on a builder page by using the following method:

  1. Assign the datatable-specific data array to a div(s) using the data attribute, identified using a datatable class: image
  2. Load the datatable source files using the following client script:

    
    // Function to load a file (JavaScript or CSS)
    function loadFile(url, type, callback) {
    const element = document.createElement(type === 'script' ? 'script' : 'link');
    
    if (type === 'script') {
        element.type = 'text/javascript';
        element.src = url;
        element.onload = callback;
    } else if (type === 'style') {
        element.rel = 'stylesheet';
        element.type = 'text/css';
        element.href = url;
        callback();  // CSS files don't have onload events in some browsers, so we call the callback immediately
    }
    
    document.head.appendChild(element);
    }

// Function to load an array of files sequentially function loadFiles(files, callback) { if (files.length === 0) { callback(); return; }

const file = files[0];
loadFile(file.url, file.type, function() {
    loadFiles(files.slice(1), callback);
});

}

// Example usage const filesToLoad = [ { url: 'https://unpkg.com/frappe-datatable@latest/dist/frappe-datatable.min.css', type: 'style' }, { url: 'https://unpkg.com/hyperlist@latest', type: 'script' }, { url: 'https://unpkg.com/lodash@latest', type: 'script' }, { url: 'https://unpkg.com/sortablejs@latest', type: 'script' }, { url: 'https://unpkg.com/frappe-datatable@latest', type: 'script' } ];

loadFiles(filesToLoad, function() { // Dispatch a custom event const event = new CustomEvent('datatableReady'); document.dispatchEvent(event); });

3. As soon as the `datatableReady` event is emitted, you can use a data-specific script to replace the divs/placeholder(s) with actual datatable element(s):
```javascript
document.addEventListener('datatableReady', function () {
    // Get all the divs with the class datatable
    let datatables = document.querySelectorAll('.datatable');

    datatables.forEach(element => {
        const dataString = element.getAttribute('data');
        const data = eval(dataString);

        const datatable = new DataTable(element, {
            columns: [
                {
                    name: 'Division',
                    id: 'division',
                    editable: false,
                    resizable: false,
                    sortable: false,
                    focusable: false,
                    dropdown: false,
                    width: 1,
                },
                {
                    name: 'Field',
                    id: 'field',
                    editable: false,
                    resizable: false,
                    sortable: false,
                    focusable: false,
                    dropdown: false,
                    width: 1,
                },
                {
                    name: 'Teams',
                    id: 'teams',
                    editable: false,
                    resizable: false,
                    sortable: false,
                    focusable: false,
                    dropdown: false,
                    width: 3,
                },
                {
                    name: 'Time',
                    id: 'time',
                    editable: false,
                    resizable: false,
                    sortable: false,
                    focusable: false,
                    dropdown: false,
                    width: 2,
                }
            ],
            data: data,
            clusterize: false,
            layout: 'ratio'
        });
    });
});

If anyone has a less-hacky approach to suggest, that would be great!