npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

ember-powerful-table

v0.0.5

Published

Easy way to dinamically generate tables.

Downloads

5

Readme

ember-powerful-table

Easy way to dinamically generate tables.

Usage

It's simple: just create a settings object and pass it to powerful-table component.

Let's ignore styles for while, we'll talk about it at the end of this document.
Important now are the features!

Basic

Configure the columns and rows.

// controllers/test.js
export default Ember.Controller.extend({
	myTableSettings: {
		columns: [
			{ title: 'Name', fields: 'name' },
			{ title: 'Lastname', fields: 'lastname' },
		],
		rows: [
			{ name: 'Bruno', lastname: 'Salgado' },
			{ name: 'Pri', lastname: 'Lopes' },
		],
	},
});

Send the settings to powerful-table component.

{{!-- templates/test.hbs --}}
{{powerful-table table=myTableSettings}}

Result
Basic

|Setting|Type|Description| |-|-|-| |columns|array of objects|the columns the table will show| |columns[].title|string|text on header| |columns[].fields|string or array of strings|corresponding information on rows| |rows|array of objects|data to fill the table|

Asynchronous Rows

Configure rows as a promise.

export default Ember.Controller.extend({
    myTableSettings: Ember.computed(function(){
        return {
            columns: [
                { title: 'Name', fields: 'name' },
                { title: 'Lastname', fields: 'lastname' },
            ],
            rows: this.loadPeople(),
        }
    }),

    loadPeople() {
        return new Ember.RSVP.Promise((resolve, reject) => {
            Ember.run.later(() => {
                resolve([
                    { name: 'Gustavo', lastname: 'Salgado' },
                    { name: 'Miguel', lastname: 'Salgado' },
                ]);
            }, 3000);
        });
    },
});

|Setting|Type|Description| |-|-|-| |rows|promise|wait the promise resolve to fill the table|

Formatting Values

The original values on rows can be formatted before showed using columns[].valueFormat.

export default Ember.Controller.extend({
	myTableSettings: {
		columns: [
			{ title: 'Name', fields: 'name' },
			{ title: 'Lastname', fields: 'lastname' },
			{
				title: 'Money',
				fields: 'money',
				valueFormat: ({value}) => `$${value.toFixed(2)}`,
			},
		],
		rows: [
			{ name: 'Bruno', lastname: 'Salgado', money: 100.73 },
			{ name: 'Pri', lastname: 'Lopes', money: 50 },
		],
	},
});

Result
Formatting Value

|Setting|Type|Description| |-|-|-| |columns[].valueFormat|function|receives the value and return it formatted|

Intrinsic Value Formats

powerful-table have some native formatations.
The table automatically transforms the value and apply some styles to the column.

export default Ember.Controller.extend({
	myTableSettings: {
		columns: [
			{ 
			    title: 'Number', 
			    fields: 'number', 
			    valueFormat: 'number' 
			},
			{ 
			    title: 'Percentage', 
			    fields: 'percentage', 
			    valueFormat: 'percentage'
			},
			{ 
			    title: 'Currency', 
			    fields: 'currency', 
			    valueFormat: 'currency'
			},
			{ 
			    title: 'Date', 
			    fields: 'date', 
			    valueFormat: 'date'
			},
		],
		rows: [
			{ 
			    number: 10, 
			    percentage: 23.7, 
			    currency: 100.51, 
			    date: '2017-09-30' 
			}
		]
	},
});

Result
Intrinsic Value Format

|Setting|Type|Description| |-|-|-| |columns[].valueFormat|string|options: number, percentage, currency or date|

Custom Intrinsic Value Formats

The intrinsic formats have custom options.

export default Ember.Controller.extend({
	myTableSettings: {
		columns: [
			{ 
			    title: 'Number', 
			    fields: 'number', 
			    valueFormat: {
					format: 'number',
					decimals: 0,
				},
			},
			{ 
			    title: 'Percentage', 
			    fields: 'percentage', 
			    valueFormat: {
					format: 'percentage',
					decimals: 0,
					symbol: '@', // "@" is just for simulation
				}
			},
			{ 
			    title: 'Currency', 
			    fields: 'currency', 
			    valueFormat: {
					format: 'currency',
					decimals: 0,
					symbol: '€',
				}
			},
			{ 
			    title: 'Date', 
			    fields: 'date', 
			    valueFormat: {
					format: 'date',
					input: 'YYYY-MM-DD',
					output: 'MMMM, DD of YYYY',
				}
			},
		],
		rows: [
			{ 
			    number: 10, 
			    percentage: 23.7, 
			    currency: 100.51, 
			    date: '2017-09-30' 
			}
		]
	},
});

Result
Intrinsic Value Format

|Setting|Type|Description| |-|-|-| |columns[].valueFormat|object|formatation settings| |columns[].valueFormat.format|string|options: number, percentage, currency or date|

Extra options based on format

|Format|Option|Description| |-|-|-| |number|decimals|decimal places count| |percentage|decimals|decimal places count| |percentage|symbol|char to concat with the value| |currency|decimals|decimal places count| |currency|symbol|char to concat with the value| |date|input|input date format(s) (see: momentjs)| |date|ouput|output date format (see: momentjs)|

Global Instrinsic Value Formats

You can configure valueFormat settings for all powerful-table on environment.

// config/environment.js
module.exports = function(environment) {
  let ENV = {
    ...
    
    APP: {
      ...
      table: {
        valueFormats: {
          number: {
            decimals: 1,
          },
          percentage: {
            decimals: 1,
            symbol: '',
          },
          currency: {
            decimals: 1,
            symbol: 'R$',
          },
          date: {
            input: 'MM/DD/YYYY',
            output: 'DD/MM/YYYY',
          },
        },
      },
    }
  };
  
  ...
  return ENV;
};

Result
Intrinsic Value Format

Composed Values

The columns[].valueFormat also receives all row data.
You can use to decide the final value or to group values.

export default Ember.Controller.extend({
	myTableSettings: {
		columns: [
			{ 
				title: 'Name',
				valueFormat: ({row}) => `${Ember.get(row, 'name')} ${Ember.get(row, 'lastname')}`,
			},
		],
		rows: [
			{ name: 'Bruno', lastname: 'Salgado' },
			{ name: 'Pri', lastname: 'Lopes' },
		],
	},
});

Result
Composed Value

|Setting|Type|Description| |-|-|-| |columns[].valueFormat|function|receives all row data and return a value|

Computed Values

To optimize the process columns[].valueFormat is called only when row is set.
If you want to observe some data to change your final value use columns[].valueComputed setting.

export default Ember.Controller.extend({
	myTableSettings: {
		columns: [
			{ title: 'Value 1', fields: 'value1' },
			{ title: 'Value 2', fields: 'value2' },
			{ 
				title: 'Total', 
				fields: 'total',
				valueFormat: ({row}) => Ember.get(row, 'value1') * Ember.get(row, 'value2'),
				valueComputed: ['value1', 'value2'],
			},
		],
		rows: [
			{ value1: 10, value2: 20 },
		],
	},
	
	init() {
		this._super(...arguments);
		setTimeout(() => {
			Ember.set(this, 'table.rows.0.value1', 20);
			Ember.set(this, 'table.rows.0.value2', 30);
		}, 3000);
	},
});

First state
Value Computed
Second state (after 3 seconds)
Value Computed

export default Ember.Controller.extend({
	myTableSettings: {
		columns: [
			{ 
				title: 'Value 1', 
				fields: 'value1', 
				valueComputed: 'value1',
			},
			{ 
				title: 'Value 2', 
				fields: 'value2', 
				valueComputed: 'value2',
			},
			{ 
				title: 'Total', 
				fields: 'total',
				valueFormat: ({row}) => Ember.get(row, 'value1') * Ember.get(row, 'value2'),
				valueComputed: ['value1', 'value2'],
			},
		],
		rows: [
			{ value1: 10, value2: 20 },
		],
	},
	
	init() {
		this._super(...arguments);
		setTimeout(() => {
			Ember.set(this, 'table.rows.0.value1', 20);
			Ember.set(this, 'table.rows.0.value2', 30);
		}, 3000);
	},
});

First state
Value Computed
Second state (after 3 seconds)
Value Computed

|Setting|Type|Description| |-|-|-| |columns[].valueComputed|string or array of strings|observe the data and call valueFormat on every changing|

Component Values

For a advanced value presentation you can define a component to receive and show the final value.

export default Ember.Controller.extend({
	myTableSettings: {
		columns: [
			{ 
				title: 'Product', 
				fields: 'product', 
			},
			{ 
				title: 'Stars', 
				fields: 'stars',
				component: 'column-stars',
			},
		],
		rows: [
			{
				product: 'Beer',
				stars: 5,
			},
			{
				product: 'Socks',
				stars: 1,
			},
		],
	},
});
// components/column-stars.js
export default Ember.Component.extend({
	_stars: Ember.computed('value', function(){
		let value = Ember.get(this, 'value');
		return new Array(value);
	}),
});
{{!-- templates/components/column-stars.hbs --}}
{{#each _stars as |star|}}
	<span>&#9733;</span>
{{/each}}

Result
Component Value

|Setting|Type|Description| |-|-|-| |columns[].component|string|path to component; it receives value (original value), formatedValue (value returned by valueFormat), col (column settings) and row (row data)|

Sorting

All columns (with fields attribute filled) are sortable by default.
To disable the sorting on a column use columns[].sorting.enabled setting.

export default Ember.Controller.extend({
	myTableSettings: {
		columns: [
			{ 
				title: 'Name', 
				fields: 'name',
				sorting: { enabled: false },
			},
			{ title: 'Lastname', fields: 'lastname' },
		],
		rows: [
			{ name: 'Bruno', lastname: 'Salgado' },
			{ name: 'Pri', lastname: 'Lopes' },
		],
	},
});

Result
Sorting

|Setting|Type|Description| |-|-|-| |columns[].sorting|object|sorting settings| |columns[].sorting.enabled|boolean|define if the column can be sorted|

Initial Sorting

Show sorted rows on init using sorting settings.

export default Ember.Controller.extend({
	myTableSettings: {
		sorting: {
			col: 0,
			order: 'desc',
		},
		columns: [
			{ title: 'Name', fields: 'name' },
			{ title: 'Lastname', fields: 'lastname' },
		],
		rows: [
			{ name: 'Bruno', lastname: 'Salgado' },
			{ name: 'Pri', lastname: 'Lopes' },
		],
	},
});

Result
Initial Sorting

|Setting|Type|Description| |-|-|-| |sorting|object|sorting settings| |sorting.col|number|column index| |sorting.order|string|ordination: 'asc' or 'desc'|

Trigger Action

Create an action clicking on the cell value.
You can open a modal, do a route transition or whatever you want/need.

export default Ember.Controller.extend({
	myTableSettings: {
		columns: [
			{ title: 'Name', fields: 'name' },
			{ title: 'Lastname', fields: 'lastname' },
			{
				fields: 'age',
				valueFormat: () => 'get age',
				trigger: ({value, row}) => alert(`${Ember.get(row, 'name')} is ${value} years old`)
			}
		],
		rows: [
			{ name: 'Bruno', lastname: 'Salgado', age: 32 },
			{ name: 'Pri', lastname: 'Lopes', age: 29 },
		],
	},
});

Result
Trigger

|Setting|Type|Description| |-|-|-| |columns[].trigger|function|triggered on cell click; it receives value (original value), formatedValue (value returned by valueFormat), col (column settings) and row (row data)|

Show Details

A column can open/close details using columns[].details settings.
powerful-table send a showDetails status to columns[].valueFormat, so you can change the cell value.

export default Ember.Controller.extend({
	myTableSettings: {
		columns: [
			{
				details: 'row-detail',
				valueFormat: ({showDetails}) => showDetails ? Ember.String.htmlSafe('&#8681;') : Ember.String.htmlSafe('&#8680;'),
				valueComputed: ['showDetails'],
			},
			{ title: 'Name', fields: 'name' },
			{ title: 'Lastname', fields: 'lastname' },
		],
		rows: [
			{ name: 'Bruno', lastname: 'Salgado', description: 'I am a developer' },
			{ name: 'Pri', lastname: 'Lopes', description: 'I love my kids' },
		],
	},
});
{{!-- templates/components/row-detail.hbs --}}
{{row.description}}

Result
Details
Details

|Setting|Type|Description| |-|-|-| |columns[].details|string|path to the component| |columns[].valueFormat|function|receives showDetails status|

Details callbacks

Do an action when a detail is opened or closed.

export default Ember.Controller.extend({
	myTableSettings: {
		columns: [
			{
				details: {
					component: 'row-detail',
					onOpen: ({row}) => alert(`${Ember.get(row, 'name')} details opened!`),
					onClose: ({row}) => alert(`${Ember.get(row, 'name')} details closed!`),
				},
				valueFormat: ({showDetails}) => showDetails ? htmlSafe('&#8681;') : htmlSafe('&#8680;'),
				valueComputed: ['showDetails'],
			},
			{ title: 'Name', fields: 'name' },
			{ title: 'Lastname', fields: 'lastname' },
		],
		rows: [
			{ name: 'Bruno', lastname: 'Salgado', description: 'I am a developer' },
			{ name: 'Pri', lastname: 'Lopes', description: 'I love my kids' },
		],
	},
});

Result
Details Callback
Details Callback

|Setting|Type|Description| |-|-|-| |columns[].details|object|details settings| |columns[].details.component|string|path to the component| |columns[].details.onOpen|function|triggered when details is opened| |columns[].details.onClose|function|triggered when details is closed|

Row Details x Column Details

It's possible to manage the formated value by the showDetails status of a row, a column or the especific cell.

export default Ember.Controller.extend({
	myTableSettings: {
		columns: [
			{ title: 'Name', fields: 'name' },
			{ title: 'Lastname', fields: 'lastname' },
			{
			    //watch if this row is showing details (any column)
				details: 'row-detail',
				valueFormat: ({row}) => row.showDetails ? Ember.String.htmlSafe('&#9899;') : Ember.String.htmlSafe('&#9898;'),
				valueComputed: ['showDetails'],
			},
			{
			    //watch if this especific cell is showing details
				details: 'row-detail',
				valueFormat: ({showDetails}) => showDetails ? Ember.String.htmlSafe('&#9899;') : Ember.String.htmlSafe('&#9898;'),
				valueComputed: ['showDetails'],
			},
			{
			    //watch if this column is showing details (any row)
				details: 'row-detail',
				valueFormat: ({col}) => col.showDetails ? Ember.String.htmlSafe('&#9899;') : Ember.String.htmlSafe('&#9898;'),
				valueComputed: ['showDetails'],
			}
		],
		rows: [
			{ name: 'Bruno', lastname: 'Salgado', description: 'I am a developer' },
			{ name: 'Pri', lastname: 'Lopes', description: 'I love my kids' },
		],
	},
});

Result
Row Details x Col Details
Clicking on first detail column
Row Details x Col Details
Clicking on second detail column
Row Details x Col Details
Clicking on third detail column
Row Details x Col Details

|Parameter|Type|Description| |-|-|-| |row.showDetails|boolean|indicate if some column is showing details in this row| |col.showDetails|boolean|indicate if some detail is open on this column, independent of the row| |showDetails|boolean|indicate if the especific cell is showing details|

Details table

Show a powerful-table as a detail.

export default Ember.Controller.extend({
	myTableSettings: {
		columns: [
			{
				details: 'orders-detail',
				valueFormat: ({showDetails}) => showDetails ? Ember.String.htmlSafe('&#8681;') : Ember.String.htmlSafe('&#8680;'),
				valueComputed: ['showDetails'],
			},
			{ title: 'Name', fields: 'name' },
			{ title: 'Lastname', fields: 'lastname' },
		],
		rows: [
			{ 
				name: 'Bruno', 
				lastname: 'Salgado',
				orders: [
					{
						id: 1,
						total: 1730,
						items: [
							{ id: 1, description: 'Notebook', price: 1000 },
							{ id: 2, description: 'Beer', price: 730 },
						],
					},
				],
			},
			{ 
				name: 'Pri', 
				lastname: 'Lopes', 
				orders: [
					{
						id: 2,
						total: 324.57,
						items: [
							{ id: 3, description: 'Shoes', price: 300 },
							{ id: 4, description: 'Wine', price: 24.57 },
						],
					},
				],
			},
		],
	},
});
// components/orders-detail.js
export default Ember.Component.extend({
	tableOrdersSettings: Ember.computed(function(){
		return {
			columns: [
				{
					details: 'order-items-detail',
					valueFormat: ({showDetails}) => showDetails ? Ember.String.htmlSafe('&#8681;') : Ember.String.htmlSafe('&#8680;'),
					valueComputed: ['showDetails'],
				},
				{ title: 'ID', fields: 'id' },
				{ 
					title: 'Total',
					fields: 'total',
					valueFormat: ({value}) => `$${value.toFixed(2)}`,
				},
			],
			rows: Ember.get(this, 'row.orders'),
		};
	}),
});
{{!-- templates/components/orders-detail.hbs --}}
<strong>Orders:</strong>
{{powerful-table table=tableOrdersSettings}}
// components/order-items-detail.js
export default Ember.Component.extend({
	tableItemsSettings: Ember.computed(function(){
		return {
			columns: [
				{ title: 'ID', fields: 'id' },
				{ title: 'Item', fields: 'description' },
				{ 
					title: 'Price',
					fields: 'price',
					valueFormat: ({value}) => `$${value.toFixed(2)}`,
				},
			],
			rows: Ember.get(this, 'row.items'),
		};
	}),
});
{{!-- templates/components/order-items-detail.hbs --}}
<strong>Items:</strong>
{{powerful-table table=tableItemsSettings}}

First state
Details Table
Person details
Details Table
Order details
Details Table

No Results

Settings to handle when the table has no data.

export default Ember.Controller.extend({
	myTableSettings: {
		columns: [
			{ title: 'Name', fields: 'name' },
			{ title: 'Lastname', fields: 'lastname' },
		],
		rows: [],
		noResults: {
			message: 'No data found',
		},
	},
});

Result
No Results

|Setting|Type|Description| |-|-|-| |noResults|object|settings for a empty table| |noResults.message|string|text to warn about empty data|

Global No Results

Configure noResults settings for all powerful-table on environment.

// config/environment.js
module.exports = function(environment) {
  let ENV = {
    ...
    APP: {
      ...
      table: {
        noResults: {
          message: 'No data found',
        },
      },
    }
  };

  ...
  return ENV;
};

Styling

Make it pretty!

export default Ember.Controller.extend({
	myTableSettings: {
		columns: [
			{ 
				title: 'Date', 
				fields: 'date', 
				valueFormat: 'date',
				headClass: 'date',
				bodyClass: 'date',
			},
			{ 
				title: 'Description', 
				fields: 'description',
				headClass: 'description',
				bodyClass: 'description',
			},
			{ 
				title: 'Value', 
				fields: 'value', 
				valueFormat: 'currency',
				headClass: 'value',
				bodyClass: {
					'value-positive:value-negative': ({value}) => value >= 0,
				},
			},
		],
		rows: [
			{
				date: '2017-09-01',
				description: 'Salary',
				value: 990,
			},
			{
				date: '2017-09-05',
				description: 'Restaurant',
				value: -100.5,
			},
			{
				date: '2017-09-07',
				description: 'Gasoline',
				value: -37,
			},
		],
	},
});
/* app/styles/app.css */
.ember-powerful-table {
	border: 1px solid #ccc;
	font-family: Tahoma;
	font-size: 12px;
}

.ember-powerful-table thead {
	background-color: #ccc;
}

.ember-powerful-table tr:nth-child(even) {
	background-color: #f2f2f2;
}

.ember-powerful-table th.date:before {
	content: '#';
	display: inline-block;
}
.ember-powerful-table td.date {
	font-weight: bold;
}

.ember-powerful-table .description {
	text-align: center;
}

.ember-powerful-table th.value {
	text-align: right;
}
.ember-powerful-table td.value-positive {
	color: blue;
}
.ember-powerful-table td.value-negative {
	color: red;
}

Result
Styling

|Setting|Type|Description| |-|-|-| |headClass|string|header css classes| |bodyClass|string|body css classes| |bodyClass|object|the attribute name is the css classes and the value is a function which determine if it's to apply; separate the classes names by ':' to apply true or false classes, respectively; it receives value (original value), formatedValue (value returned by valueFormat), col (column settings) and row (row data)|