muonw / muonw-powertable

▦ PowerTable is a Svelte component that turns JSON data into an interactive HTML table. Inspired by DataTables. Powered by Svelte.
https://muonw.github.io/muonw-powertable/
Other
220 stars 13 forks source link

customParse gives an error #17

Closed haenf closed 1 year ago

haenf commented 1 year ago

Muonw, it's a beautiful piece of software, but now I'm stuck on a detail where I can't find a solution:

I have a table where items (like windmills in the example below) have their data stacked over more than one line. So in the example the first 4 rows belong together, then the next three, and so forth. I want the label hasLabel (nl) of the first row of a stack to be in bold, to make things easier to distinguish.

image

I tried to do this with the following code, mainly inspired on your example 2.

<script>
    import { PowerTable } from '@muonw/powertable';
    import { storedJsonTable } from '$lib/utils/stores';

    let ptData = $storedJsonTable
    export let ptInstructs;

    let ptOptions = {
        userFunctions: {
            customParse: setCellStyle
        },
        headerFilters: false,
        footerText: false,
        rowsPerPageOptions: [10, 100, 1000, 10000, 100000],
        rowsPerPage: 100,
        segments: {
            'topBar': ['search', 'dropdown', 'stats', 'pagination'],
            'pTable': ['table'],
            'bottomBar': [ ],
        }
    }

    function setCellStyle(pageData) {
        let label1 = 'hasLabel (nl)'   // for testing

        pageData.forEach((row, i) => {
            if (i === 0) {
                console.log('cur row: ' + pageData[0][label1]);
                pageData[i][label1] = `<b>Object.values(pageData[i])[label1]</b>`;
            }
            if (i > 0 && pageData[i][label1] !== pageData[i-1][label1]) {
                console.log('cur row: ' + pageData[i][label1]); 
                pageData[i][label1] = `<b>pageData[i][label1']</b>`;              
            }
        }); 
    }

</script>

<div class="MuonW PowerTable ptStyles1">
    <PowerTable ptData = {ptData} {ptInstructs} ptOptions = {ptOptions} />
</div>

But the above code results in an error - even after trying many different variations:

image

The pageData.forEach statements are executed all the way (see the console.log lines at the top of the screendump), but then things are blocked by the can't access property "length", ctx[11] is undefined error. This error happens no matter what I put in pageData.forEach. The error only isn't there if I comment out the userFunctions: { customParse: setCellStyle, }, in the ptOptions.

Do you have any idea what goes wrong?

And do you think there is a way to give grouped rows equal alternating background colors? That would even be nicer than the above solution...

muonw-public commented 1 year ago

I don't know why you get that error, but I found some issues with your code. I give you a code that I expect to work and you can compare it with yours. The key point is that when you find the first row of the stack, you should loop through and modify each column in that row. And at the end, you would want to return the modified array. Something like this:

function setCellStyle(pageData) {
    let groupByKey = 'hasLabel (nl)';
    let latestGroupName = null;

    pageData.forEach((row, rowIndex) => {
        if ((row[groupByKey] !== latestGroupName)) {
            latestGroupName = row[groupByKey];

            Object.entries(row).forEach(([instructKey, content]) => {
                // Here we exclude the PowerTable's internal instruct keys (they begin with "__PT_"). Sorry, this is not well documented!
                if (instructKey.indexOf('__PT_') !== 0) {
                    pageData[rowIndex][instructKey] = `<b>${content}</b>`;
                }
            });
        }
    }); 

    return pageData;
}

This code assumes that hasLabel (nl) is an instruct key, not just title. Also, note that since you are adding HTML tags, your instructs need to have parseAs set to unsafe-html to render the HTML.

let ptInstructs = [
    {key: 'hasLabel (nl)', title: 'hasLabel (nl)', parseAs: 'unsafe-html'},
    ...

For groups of rows to have alternating backgrounds, you may be able to define a boolean variable to keep track of odd and even groups. For example, let isOdd = true;. Then run isOdd = !isOdd; whenever you detect a new row of a stack. After setting a CSS selector for alternating groups (e.g. <b data-stack={isOdd ? 'odd' : 'even';}>${content}</b>) you may be able to take advantage of the :has() pseudo-class to style the parent <tr>!

haenf commented 1 year ago

Thank you for your quick supply. Your code worked fine, with a small change to set only the first cell of row in bold:

function setCellStyle(pageData) {
        let groupByKey = srcHeaders[1];
        let latestGroupName = null;

        pageData.forEach((row, rowIndex) => {
            if ((row[groupByKey] !== latestGroupName)) {
                latestGroupName = row[groupByKey];

                Object.entries(row).forEach(([instructKey, content]) => {
                    // Here we exclude the PowerTable's internal instruct keys (they begin with "__PT_")
                    if (instructKey.indexOf('__PT_') !== 0) {
                        pageData[rowIndex][srcHeaders[1]] = 
                            `<span style="font-weight:700">${pageData[rowIndex][srcHeaders[1]]}</span>`;                        
                    }
                });
            }
        }); 

        return pageData;
    }

</script>

The srcHeader array is derived directly from the imported data, and the srcHeader[1] is indeed an instruct key.

The result:

image

I am very happy with the result! One more little question: I managed to get a satisfactory styling by just using the css. But I couldn't find the css part for the row selector. What are the css properties to style the widget with?

muonw-public commented 1 year ago

Thank you for sharing. If you need only the first cell to be bold you don't have to loop through the columns. So you can replace...

Object.entries(row).forEach(([instructKey, content]) => {
   // Here we exclude the PowerTable's internal instruct keys (they begin with "__PT_")
        if (instructKey.indexOf('__PT_') !== 0) {
            pageData[rowIndex][srcHeaders[1]] = 
            `<span style="font-weight:700">${pageData[rowIndex][srcHeaders[1]]}</span>`;                        
    }
});

With something like this:

// Here we exclude the PowerTable's internal instruct keys (they begin with "__PT_")
if ((pageData[rowIndex][srcHeaders[0]]).indexOf('__PT_') !== 0) {
    pageData[rowIndex][srcHeaders[1]] = 
    `<span style="font-weight:700">${pageData[rowIndex][srcHeaders[1]]}</span>`;                        
}

To select the rows you can try this:

.MuonW.PowerTable tbody tr {
    // your styles
}