https://github.com/joffreyBerrier/vuejs-spreadsheet/releases/tag/1.9.1
https://medium.com/scalia/vuejs-spreadsheet-692cab2cb5c8
👊 An easier Spreadsheet in Vue.js 👊
Do not hesitate to ⭐ my repo
yarn add vuejs-spreadsheet
npm i vuejs-spreadsheet
<script>
import VueTable from 'vuejs-spreadsheet';
export default {
name: 'app',
data() {
},
components: {
VueTable,
},
};
</script>
- First, fork the repo on github.
- Clone your forked repo and run:
yarn
ornpm i
- Then, make your changes on any branch you want and push it.
- Finally, open a pull request on the official repo, using the source branch from your forked repo.
Please do not push the package-lock.json
.
Props | Type | Description |
---|---|---|
:tbody-data | Array | That contains data |
:headers | Array | That contains headers |
:custom-options | Object | That contains Options |
:style-wrap-vue-table | Object | That contains style of the wrapper tableVue |
:disable-cells | Array | That contains the headerKey you want to disable |
:disable-sort-thead | Array | That contains the disabled th |
:loading | Boolean | True => Hidden TbodyData / show slot loader |
:parent-scroll-element | Object | That contains the HTML attribute which overflow-y: scroll (by-default is 'html') |
:select-position | Object | That contains a top and left position you want to add to the select |
:submenu-tbody | Array | That contains the submenu-tbody |
:submenu-thead | Array | That contains the submenu-thead |
Options | Type | Description |
---|---|---|
:fuse-options | Object | That contains an object of fuse configuration look on her website: http://fusejs.io/ |
:new-data | Object | That contains the type of data when you have empty cell in a row |
:sort-header | Boolean | That activates sort button on header |
:tbody-index | Boolean | That displays the index of each row on the left of the table |
:trad | Object | That contains an object of translating |
Function | Type | Description |
---|---|---|
v-on:tbody-all-checked-row | Function | Fired when the checkedAll row has checked |
v-on:tbody-checked-row | Function | Fired when row has checked |
v-on:tbody-change-data | Function | Fired when data undergo modifications |
v-on:tbody-input-change | Function | When the input changes |
v-on:tbody-select-change | Function | When the select change |
v-on:handle-up-drag-size-header | Function | Fired when the header size changed |
v-on:thead-td-sort | Function | When you press the button sort |
v-on:tbody-up-dragtofill | Function | Fired when pressed up on dragToFill |
v-on:tbody-move-dragtofill | Function | Fired when moved on dragToFill |
v-on:tbody-nav-backspace | Function | When you press backspace on cell (event, actualElement, actualCol, rowIndex, colIndex) |
v-on:tbody-nav-multiple-backspace | Function | Fired when the multiple cell are delete |
v-on:tbody-submenu-click-{#} | Function | {#} - Name of the function declared on submenu-tbody |
<vue-table
:tbody-data="Array"
:headers="Array"
:custom-options="Object"
:style-wrap-vue-table="Object"
:disable-cells="Array"
:disable-sort-thead="Array"
:loading="Boolean"
:parent-scroll-element="Object"
:select-position="Object"
:submenu-tbody="Array"
:submenu-thead="Array"
v-on:tbody-checked-row="checkedData"
v-on:tbody-all-checked-row="checkedAllData"
v-on:tbody-change-data="changeData"
v-on:tbody-submenu-click-change-color="changeColorTbody"
v-on:tbody-submenu-click-change-value="changeValueTbody"
v-on:thead-submenu-click-change-color="changeColorThead"
v-on:thead-submenu-click-change-value="changeValueThead"
v-on:thead-td-sort="sortProduct">
// if your want to add an specific header
<div slot="header">
Specific Header
</div>
// if your want to add a loader
<div slot="loader">
Loader
</div>
</vue-table>
customOptions: {
dragToFill: true,
tbodyCheckbox: false,
tbodyIndex: true,
sortHeader: true,
trad: {
lang: 'fr',
en: {
select: {
placeholder: 'Search by typing',
},
},
fr: {
select: {
placeholder: 'Taper pour chercher',
},
},
},
newData: {
type: 'input',
value: '',
active: false,
style: {
color: '#000',
},
},
fuseOptions: {
shouldSort: true,
threshold: 0.2,
location: 0,
distance: 30,
maxPatternLength: 64,
minMatchCharLength: 1,
findAllMatches: false,
tokenize: false,
keys: [
'value',
],
},
},
If you want to use the commentBox (like excel)
Create an object comment: {}
on styleWrapVueTable
and on each data
styleWrapVueTable: {
...
comment: {
borderColor: '#696969',
borderSize: '8px',
widthBox: '120px',
heightBox: '80px',
},
},
CommentBox without content:
f: {
...
comment: {
borderColor: '#eee',
},
...
},
CommentBox with content:
f: {
...
comment: {
value: 'comment',
borderColor: '#eee',
},
...
},
If you want to use the checkbox
1: Create a key tbodyCheckbox: true
on customOptions
If you want to get the array of the checked data use this.$refs.vueTable.checkedRows
customOptions: {
...
tbodyCheckbox: boolean
...
},
Name | Type | Description |
---|---|---|
headerName | String | The chosen header name |
headerkey | String | The Slugify version of the headerName |
style | Object | The style of the td |
- width | String | Indicate the width of ``<th>``
- minWidth | String | minWidth must be equal to width
disabled | Boolean | optional - Disabled cell
headers: [
{
headerName: 'Image',
headerKey: 'img',
style: {
width: '100px'
minWidth: '100px'
},
},
{
headerName: 'Nom',
headerKey: 'name',
style: {
width: '100px'
minWidth: '100px'
},
},
{
headerName: 'Prénom',
headerKey: 'surname',
style: {
width: '100px'
minWidth: '100px'
},
},
{
headerName: 'Age',
headerKey: 'age',
style: {
width: '100px'
minWidth: '100px'
},
},
{
headerName: 'Born',
headerKey: 'born',
style: {
width: '100px'
minWidth: '100px'
},
},
],
Name | Type | Description |
---|---|---|
key | String | The key of the object written in Slugify |
type | String | The type of data rendered (<textarea> , <img> , <select> ) |
value(img/input) | String | The value of the object in String Type |
value(select) | Array | The value of the object in Array Type |
selectOptions | Array | That contains objects {value: ~, label: ~} |
style | Object | The Style of the cell |
active | Boolean | Of the cell, false by default |
handleSearch | Boolean | - Activates search when selected |
disabled | Boolean | optional - Disabled cell |
products: [
{
img: {
type: 'img',
value: 'https://via.placeholder.com/350x150',
active: false,
disabled: true,
},
name: {
type: 'input',
value: 'John',
active: false,
style: {
color: '#000',
},
},
surname: {
type: 'input',
value: 'Doe',
active: false,
style: {
color: '#000',
},
},
age: {
type: 'select',
handleSearch: true,
selectOptions: [
{
value: 'paris',
label: 'Paris',
},
{
value: 'new-york',
label: 'New York',
},
],
value: 'paris',
active: false,
},
born: {
type: 'select',
handleSearch: true,
selectOptions: [
{
value: 'france',
label: 'France',
},
{
value: 'usa',
label: 'United States of America',
},
],
value: 'france',
active: false,
},
},
],
Same Object describe on the top
If you choose an input
newData: {
type: 'input',
value: '',
active: false,
style: {
color: '#000',
background: '#cfffcf',
},
},
Name | Type | Description |
---|---|---|
type | String | The type of data rendered (<button> |
value | String | The value of the function |
function | String | The name of the function called when you click on the button - Written in Slugify |
disabled | Array | Each object which you want to hide on the submenu |
subtitle | String | Of the select |
selectOptions | Array | That contains objects {value: ~, label: ~} |
buttonOption | Object | Description |
. value | String | The value of the button |
. function | String | The name of the function called when you click on the button - Written in Slugify |
. style | Object | The style of the button |
submenuTbody: [
{
type: 'button',
value: 'Change Color',
function: 'change-color',
disabled: ['img'],
},
],
submenuThead: [
{
type: 'button',
value: 'Change Color',
function: 'change-color',
disabled: ['img', 'name'],
},
{
type: 'select',
disabled: ['img'],
subtitle: 'Select state:',
selectOptions: [
{
value: 'new-york',
label: 'new-york',
},
{
value: 'france',
label: 'france',
},
],
value: 'new-york',
buttonOption: {
value: 'change city',
function: 'change-city',
style: {
display: 'block',
},
},
},
],
<template>
<div id="app">
<vue-table
:tbody-data="products"
:headers="headers"
:custom-options="customOptions"
:style-wrap-vue-table="styleWrapVueTable"
:disable-cells="disableCells"
:disable-sort-thead="disableSortThead"
:loading="loading"
:parent-scroll-element="parentScrollElement"
:select-position="selectPosition"
:submenu-tbody="submenuTbody"
:submenu-thead="submenuThead"
v-on:tbody-change-data="changeData"
v-on:tbody-submenu-click-change-color="changeColorTbody"
v-on:tbody-submenu-click-change-value="changeValueTbody"
v-on:thead-submenu-click-change-color="changeColorThead"
v-on:thead-submenu-click-change-value="changeValueThead"
v-on:thead-td-sort="sortProduct">
<div slot="header">
Specific Header
</div>
<div slot="loader">
Loader
</div>
</vue-table>
</div>
</template>
<script>
import VueTable from 'vuejs-spreadsheet';
export default {
name: 'app',
data() {
return {
customOptions: {
tbodyIndex: true,
sortHeader: true,
trad: {
lang: 'fr',
en: {
select: {
placeholder: 'Search by typing',
},
},
fr: {
select: {
placeholder: 'Taper pour chercher',
},
},
},
newData: {
type: 'input',
value: '',
active: false,
style: {
color: '#000',
},
},
fuseOptions: {
shouldSort: true,
threshold: 0.2,
location: 0,
distance: 30,
maxPatternLength: 64,
minMatchCharLength: 1,
findAllMatches: false,
tokenize: false,
keys: [
'value',
],
},
},
submenuTbody: [
{
type: 'button',
value: 'change color',
function: 'change-color',
disabled: ['img'],
},
{
type: 'button',
value: 'change value',
function: 'change-value',
disabled: ['img', 'name'],
},
],
submenuThead: [
{
type: 'button',
value: 'change color',
function: 'change-color',
disabled: ['a'],
},
{
type: 'select',
disabled: ['a'],
subtitle: 'Select state:',
selectOptions: [
{
value: 'new-york',
label: 'new-york',
},
{
value: 'france',
label: 'france',
},
],
value: 'new-york',
buttonOption: {
value: 'change city',
function: 'change-city',
style: {
display: 'block',
},
},
},
{
type: 'button',
value: 'change value',
function: 'change-value',
disabled: ['a', 'b'],
},
],
disableCells: ['a'],
loading: false,
parentScrollElement: {
attribute: 'html',
positionTop: 0,
},
selectPosition: {
top: 0,
left: 0,
},
disableSortThead: ['a'],
styleWrapVueTable: {
fontSize: '12px',
comment: {
borderColor: '#696969',
borderSize: '8px',
widthBox: '120px',
heightBox: '80px',
},
},
headers: [
{
headerName: 'A',
headerKey: 'a',
style: {
width: '200px',
minWidth: '200px',
color: '#000',
},
},
{
headerName: 'B',
headerKey: 'b',
style: {
width: '200px',
minWidth: '200px',
color: '#000',
},
},
{
headerName: 'C',
headerKey: 'c',
style: {
width: '200px',
minWidth: '200px',
color: '#000',
},
},
{
headerName: 'D',
headerKey: 'd',
style: {
width: '200px',
minWidth: '200px',
color: '#000',
},
},
{
headerName: 'E',
headerKey: 'e',
style: {
width: '200px',
minWidth: '200px',
color: '#000',
},
},
{
headerName: 'F',
headerKey: 'f',
style: {
width: '200px',
minWidth: '200px',
color: '#000',
},
},
{
headerName: 'G',
headerKey: 'g',
style: {
width: '200px',
minWidth: '200px',
color: '#000',
},
},
],
products: [
{
a: {
type: 'img',
value: 'https://via.placeholder.com/350x150',
active: false,
},
c: {
type: 'input',
value: 'Paris',
active: false,
style: {
color: '#000',
},
},
d: {
type: 'input',
value: 'France',
active: false,
style: {
color: '#000',
},
},
e: {
type: 'input',
value: 'Boe',
active: false,
style: {
color: '#000',
},
},
f: {
type: 'select',
handleSearch: true,
selectOptions: [
{
value: 'Harry Potter',
label: 'harry potter',
},
{
value: 'Hermione Granger',
label: 'hermione granger',
},
{
value: 'Ron Whisley',
label: 'ron whisley',
},
{
value: 'Dobby',
label: 'dobby',
},
{
value: 'Hagrid',
label: 'hagrid',
},
{
value: 'Professeur Rogue',
label: 'professeur rogue',
},
{
value: 'Professeur Mcgonagal',
label: 'professeur mcgonagal',
},
{
value: 'Professeur Dumbledor',
label: 'professeur dumbledor',
},
],
value: 'professeur dumbledor',
active: false,
},
g: {
type: 'select',
handleSearch: true,
selectOptions: [
{
value: 1980,
label: 1980,
},
{
value: 1981,
label: 1981,
},
{
value: 1982,
label: 1982,
},
{
value: 1983,
label: 1983,
active: true,
},
{
value: 1984,
label: 1984,
},
],
value: 1983,
active: false,
},
},
],
};
},
components: {
VueTable,
},
mounted() {
this.loading = true;
setTimeout(() => {
this.loading = false;
}, 300);
},
methods: {
changeData(row, header) {
console.log(row, header);
},
sortProduct(event, header, colIndex) {
console.log('sort product');
},
// callback
changeColorThead(event, header, colIndex) {
this.headers[colIndex].style.color = '#e40000';
},
changeColorTbody(event, header, rowIndex, colIndex) {
this.products[rowIndex][header].style = {};
this.products[rowIndex][header].style.color = '#e40000';
},
changeValueTbody(event, header, rowIndex, colIndex) {
this.products[rowIndex][header].value = 'T-shirt';
},
changeValueThead(event, entry, colIndex) {
this.headers[colIndex].headerName = 'T-shirt';
},
},
};
</script>
<style lang="scss">
::-moz-selection {
color: #2c3e50;
background: transparent;
}
::selection {
color: #2c3e50;
background: transparent;
}
</style>