arnedesmedt / vue-ads-table-tree

MIT License
125 stars 30 forks source link

vue-ads-table-tree

Vue ads table tree is a vue js table component, with a tree functionality. Here is a list of all the features of this table:

You can use 2 components:

Demo

I've written a demo in JSFiddle

Installation

You can install the package via npm or yarn.

NPM

npm install vue-ads-table-tree --save

YARN

yarn add vue-ads-table-tree

General objects for the components

Columns

Columns are listed in a column array as plain objects and can have the following arguments:

Rows

Rows are listed in a row array as plain objects. Each object contains the row data and meta data. Meta data is prefixed with a _:

Styling

All styling is done via a plain object. It contains a selector for a row/column/cell as key and a vue based object that contains classes. The latter ones can override the earlier ones:

Examples

Table component

If you just need a normal table that you can customize fully by yourself, you can use the table component. It won't provide you a paginator, filter textbox, ... It's just a Vue component and all interaction happens via the properties or event handlers.

If you want to use the table in a predefined container with a paginator, filter textbox see the full component section

Async features

It is also possible to use this component without all data is already loaded. Therefore you will have to use the call- properties that will hold a callback function to load some additional data asynchronously.

Load your root rows asynchronously

In previous versions of the table tree, you needed to pass the total rows as a property. Now, you don't need that anymore. Just change the length of the rows array. For example you already have 3 root rows in your JSON array, but you want to load another 3 root rows asynchronously, well then set the length of your rows property to 6: this.rows.length = 6. Don't forget to add the call-rows property to your component. See properties

Load your child rows asynchronously

If you want to load your child rows later, just add the _hasChildren attribute to your row with true as value. It will add a toggle children icon and when you click it, the children will be loaded. Don't forget to add the call-children property to your component. See properties

Sort or filter on a not fully loaded table

If you're sorting/filtering while not all root/child rows are loaded. There is a small problem. We don't know if your sort or filter result will be the right one, because not all the data is loaded. Therefore the component will call the call-temp-rows function you need to pass via the property. See properties

Be aware that the result of the function call is send to the table without any additional filtering, sorting or pagination. The rows will only expand if needed. You can see it as you pass temporarily rows to the table

Properties

Start and end using the Array.slice method to show only a part of the rows. If you don't add them as properties, their value will be undefined and all the rows will be visible.

Events

Slots

1. Row/Column/Cell slot

If you want to use your own template for all cells in a row, for all cells in a column or for a specific cell, you can use your own scoped slot with the following names (the first will overrule the latter):

All three contains a slot-scope which contains the following parameters:

2. No rows slot

If no rows are loaded, the table displays 'No results found.'. You can replace this message by the scoped slot no-rows.

3. Sort icon slot

If you want to customize the sort icon add a scoped slot with the name sort-icon. The scope contains only one parameter:

4. Toggle children icon slot

If you want to customize the toggle children icon add a scoped slot with the name toggle-children-icon. The scope contains two parameters:

Example

<template>
    <div id="app">
        <vue-ads-table
            :columns="columns"
            :rows="rows"
            :classes="classes"
            :filter="filter"
            :start="start"
            :end="end"
            @filter-change="filterChanged"
            :call-rows="callRows"
            :call-children="callChildren"
            :call-temp-rows="callTempRows"
        >
            <!-- Will be applied on the name column for the rows with an _id of tiger -->
            <template slot="name_tiger" slot-scope="props">test cell - {{ props.row.name }}</template>
            <!-- Will be applied on the city column -->
            <template slot="city" slot-scope="props">test column - {{ props.row.city }}</template>
            <!-- Will be applied on the row with _id tiger -->
            <template slot="_tiger" slot-scope="props">test row - {{ props.row[props.column.property] }}</template>
            <template slot="no-rows">Geen resultaten</template>
            <template slot="sort-icon" slot-scope="props"> ({{ props.direction === null ? 'null' : (props.direction ? 'up' : 'down') }}) </template>
            <template slot="toggle-children-icon" slot-scope="props"> [{{ props.expanded ? '-' : '+' }}] </template>
        </vue-ads-table>
    </div>
</template>

<script>
import { VueAdsTable } from 'vue-ads-table-tree';

export default {
    name: 'BasicTableApp',

    components: {
        VueAdsTable,
    },

    data () {
        let rows = [
            {
                _id: 'tiger',
                name: 'Tiger Nixon',
                function: 'System Architect',
                city: 'Edinburgh',
                id: '5421',
                since: '2011/04/25',
                budget: '$320,800',
                _hasChildren: true,
            },
            {
                name: 'Lael Greer',
                function: 'Systems Administrator',
                city: 'London',
                id: '6733',
                since: '2009/02/27',
                budget: '$103,500',
                _showChildren: true,
                _children: [
                    {
                        name: 'Garrett Winters',
                        function: 'Accountant',
                        city: 'Tokyo',
                        id: '8422',
                        since: '2011/07/25',
                        budget: '$170,750',
                    },
                ],
            },
        ];

        rows.length = 4;

        let columns = [
            {
                property: 'id',
                title: 'ID#',
                direction: null,
                filterable: true,
            },
            {
                property: 'name',
                title: 'Name',
                direction: null,
                filterable: true,
            },
            {
                 property: 'function',
                title: 'Function',
                direction: null,
                filterable: true,
                groupable: true,
            },
            {
                property: 'city',
                title: 'City',
                direction: null,
                filterable: true,
            },
            {
                property: 'since',
                title: 'Since',
                direction: null,
                filterable: true,
            },
            {
                property: 'budget',
                title: 'Budget',
                direction: null,
                filterable: true,
            },
        ];

        let classes = {
            group: {
                'vue-ads-font-bold': true,
                'vue-ads-border-b': true,
                'vue-ads-italic': true,
            },
            '0/all': {
                'vue-ads-py-3': true,
                'vue-ads-px-2': true,
            },
            'even/': {
                'vue-ads-bg-blue-lighter': true,
            },
            'odd/': {
                'vue-ads-bg-blue-lightest': true,
            },
            '0/': {
                'vue-ads-bg-blue-lighter': false,
                'vue-ads-bg-blue-dark': true,
                'vue-ads-text-white': true,
                'vue-ads-font-bold': true,
            },
            '1_/': {
                'hover:vue-ads-bg-red-lighter': true,
            },
            '1_/0': {
                'leftAlign': true
            }
        };

        return {
            rows,
            columns,
            classes,
            filter: '',
            start: 0,
            end: 2,
        };
    },

    methods: {
        filterChanged (filter) {
            this.filter = filter;
        },

        sleep (ms) {
            return new Promise(resolve => setTimeout(resolve, ms));
        },

        async callRows (indexesToLoad) {
            await this.sleep(1000);
            return indexesToLoad.map(index => {
                return {
                    name: 'Call Rows',
                    function: 'Developer',
                    city: 'San Francisco',
                    id: '8196',
                    since: '2010/07/14',
                    budget: '$86,500',
                };
            });
        },

        async callChildren (parent) {
            await this.sleep(1000);
            return [
                {
                    name: 'Call child',
                    function: 'Developer',
                    city: 'San Francisco',
                    id: '8196',
                    since: '2010/07/14',
                    budget: '$86,500',
                },
            ];
        },

        async callTempRows (filter, columns, start, end) {
            await this.sleep(1000);
            return {
                rows: [
                    {
                        name: 'Temp call',
                        function: 'Developer',
                        city: 'San Francisco',
                        id: '8196',
                        since: '2010/07/14',
                        budget: '$86,500',
                    },
                    {
                        name: 'Temp call',
                        function: 'Developer',
                        city: 'San Francisco',
                        id: '8196',
                        since: '2010/07/14',
                        budget: '$86,500',
                    },
                ],
                total: 4,
            };
        },    
    },
};
</script>

<style>
    .leftAlign {
        text-align: left;
    }
</style>

Full component

The table container is the complete component it adds a filter box and and a paginator to the table. If your call-rows property is not empty, an async table component will be used. If the property is empty and basic table component will be used.

Properties

You can use the columns, rows, filter, classes, selectable, full-export, call-rows, call-temp-rows and call-children properties from the base and async table. But their are some additional properties:

Events

The table container has 2 event:

Slots

All the slots of the basic table can be used.

And their are 2 additional slots:

1. Top

A scoped slot that can be used to overwrite everything that is on the top of the table. It has the following scope:

2. Bottom

A scoped slot that can be used to overwrite everything that is on the bottom of the table. It has the following scope:

Testing

We use the jest framework for testing the table tree component. Run the following command to test it:

npm run test:unit

Changelog

Read the CHANGELOG file to check what has changed.

Issues

If you have any issues (bugs, features, ...) on the current project, add them here.

Contributing

Do you like to contribute to this project? Please, read the CONTRIBUTING file.

Social

Donate

Want to make a donation? That would be highly appreciated!

Make a donation via PayPal.