2019-02-12 09:08:37 +01:00
|
|
|
import './rows/renderer/Head'
|
|
|
|
import './rows/renderer/Row'
|
|
|
|
import './filter/ColumnFilter'
|
|
|
|
import './filter/RowFilter'
|
|
|
|
import {UnitOfWorkTracker} from '../mixins/UnitOfWorkTracker'
|
2019-03-14 10:30:23 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Table State
|
|
|
|
*
|
|
|
|
* Used to restore a table to a given state.
|
|
|
|
*/
|
|
|
|
class TableState {
|
|
|
|
constructor(selectedIds) {
|
|
|
|
this.selectedIds = selectedIds || [];
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Apply state to row
|
|
|
|
* @param {RowBase} rowObject
|
|
|
|
*/
|
|
|
|
applyRowState(rowObject) {
|
|
|
|
rowObject.isSelected = this.selectedIds.includes(rowObject.getId());
|
|
|
|
}
|
|
|
|
}
|
2019-02-12 09:08:37 +01:00
|
|
|
|
|
|
|
const TEMPLATE =`
|
|
|
|
<div class="pillar-table-container"
|
|
|
|
:class="$options.name"
|
|
|
|
>
|
|
|
|
<div class="pillar-table-menu">
|
|
|
|
<pillar-table-row-filter
|
2019-03-13 13:53:40 +01:00
|
|
|
:rowObjects="sortedRowObjects"
|
2019-02-12 09:08:37 +01:00
|
|
|
@visibleRowObjectsChanged="onVisibleRowObjectsChanged"
|
|
|
|
/>
|
2019-03-13 13:53:40 +01:00
|
|
|
<pillar-table-actions
|
|
|
|
@item-clicked="onItemClicked"
|
|
|
|
/>
|
2019-02-12 09:08:37 +01:00
|
|
|
<pillar-table-column-filter
|
|
|
|
:columns="columns"
|
|
|
|
@visibleColumnsChanged="onVisibleColumnsChanged"
|
|
|
|
/>
|
|
|
|
</div>
|
|
|
|
<div class="pillar-table">
|
|
|
|
<pillar-table-head
|
|
|
|
:columns="visibleColumns"
|
|
|
|
@sort="onSort"
|
|
|
|
/>
|
|
|
|
<transition-group name="pillar-table-row" tag="div" class="pillar-table-row-group">
|
|
|
|
<pillar-table-row
|
|
|
|
v-for="rowObject in visibleRowObjects"
|
|
|
|
:columns="visibleColumns"
|
|
|
|
:rowObject="rowObject"
|
|
|
|
:key="rowObject.getId()"
|
2019-03-13 13:53:40 +01:00
|
|
|
@item-clicked="onItemClicked"
|
2019-02-12 09:08:37 +01:00
|
|
|
/>
|
|
|
|
</transition-group>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
`;
|
|
|
|
|
2019-03-14 10:30:23 +01:00
|
|
|
/**
|
|
|
|
* The table renders RowObject instances for the rows, and ColumnBase instances for the Columns.
|
|
|
|
* Extend the table to fit your needs.
|
|
|
|
*
|
|
|
|
* Usage:
|
|
|
|
* Extend RowBase to wrap the data you want in your row
|
|
|
|
* Extend ColumnBase once per column type you need
|
|
|
|
* Extend RowObjectsSourceBase to fetch and initialize your rows
|
|
|
|
* Extend ColumnFactoryBase to create the rows for your table
|
|
|
|
* Extend This Table with your ColumnFactory and RowSource
|
|
|
|
*
|
|
|
|
* @emits isInitialized When all rows has been fetched, and are initialized.
|
|
|
|
* @emits selectItemsChanged(selectedItems) When selected rows has changed.
|
|
|
|
*/
|
2019-02-12 09:08:37 +01:00
|
|
|
let PillarTable = Vue.component('pillar-table-base', {
|
|
|
|
template: TEMPLATE,
|
|
|
|
mixins: [UnitOfWorkTracker],
|
|
|
|
// columnFactory,
|
|
|
|
// rowsSource,
|
|
|
|
props: {
|
2019-03-13 13:53:40 +01:00
|
|
|
projectId: String,
|
|
|
|
selectedIds: Array,
|
|
|
|
canChangeSelectionCB: {
|
|
|
|
type: Function,
|
|
|
|
default: () => true
|
|
|
|
},
|
|
|
|
canMultiSelect: {
|
|
|
|
type: Boolean,
|
|
|
|
default: true
|
|
|
|
},
|
2019-02-12 09:08:37 +01:00
|
|
|
},
|
|
|
|
data: function() {
|
|
|
|
return {
|
|
|
|
columns: [],
|
|
|
|
visibleColumns: [],
|
|
|
|
visibleRowObjects: [],
|
2019-03-13 13:53:40 +01:00
|
|
|
rowsSource: {},
|
|
|
|
isInitialized: false,
|
2019-03-14 10:30:23 +01:00
|
|
|
compareRowsCB: (row1, row2) => 0
|
2019-02-12 09:08:37 +01:00
|
|
|
}
|
|
|
|
},
|
|
|
|
computed: {
|
|
|
|
rowObjects() {
|
|
|
|
return this.rowsSource.rowObjects || [];
|
2019-03-13 13:53:40 +01:00
|
|
|
},
|
2019-03-14 10:30:23 +01:00
|
|
|
/**
|
|
|
|
* Rows sorted with a column sorter
|
|
|
|
*/
|
2019-03-13 13:53:40 +01:00
|
|
|
sortedRowObjects() {
|
2019-03-14 10:30:23 +01:00
|
|
|
return this.rowObjects.concat().sort(this.compareRowsCB);
|
2019-03-13 13:53:40 +01:00
|
|
|
},
|
|
|
|
rowAndChildObjects() {
|
|
|
|
let all = [];
|
|
|
|
for (const row of this.rowObjects) {
|
|
|
|
all.push(row, ...row.getChildObjects());
|
|
|
|
}
|
|
|
|
return all;
|
|
|
|
},
|
|
|
|
selectedItems() {
|
|
|
|
return this.rowAndChildObjects.filter(it => it.isSelected)
|
|
|
|
.map(it => it.underlyingObject);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
watch: {
|
|
|
|
selectedIds(newValue) {
|
|
|
|
this.rowAndChildObjects.forEach(item => {
|
|
|
|
item.isSelected = newValue.includes(item.getId());
|
|
|
|
});
|
|
|
|
},
|
|
|
|
selectedItems(newValue, oldValue) {
|
|
|
|
this.$emit('selectItemsChanged', newValue);
|
|
|
|
},
|
|
|
|
isInitialized(newValue) {
|
|
|
|
if (newValue) {
|
|
|
|
this.$emit('isInitialized');
|
|
|
|
}
|
2019-02-12 09:08:37 +01:00
|
|
|
}
|
|
|
|
},
|
|
|
|
created() {
|
|
|
|
let columnFactory = new this.$options.columnFactory(this.projectId);
|
|
|
|
this.rowsSource = new this.$options.rowsSource(this.projectId);
|
2019-03-13 13:53:40 +01:00
|
|
|
|
2019-03-14 10:30:23 +01:00
|
|
|
let tableState = new TableState(this.selectedIds);
|
2019-03-13 13:53:40 +01:00
|
|
|
|
2019-02-12 09:08:37 +01:00
|
|
|
this.unitOfWork(
|
|
|
|
Promise.all([
|
|
|
|
columnFactory.thenGetColumns(),
|
2019-03-14 10:30:23 +01:00
|
|
|
this.rowsSource.thenGetRowObjects()
|
2019-02-12 09:08:37 +01:00
|
|
|
])
|
|
|
|
.then((resp) => {
|
|
|
|
this.columns = resp[0];
|
2019-03-13 13:53:40 +01:00
|
|
|
return this.rowsSource.thenInit();
|
|
|
|
})
|
|
|
|
.then(() => {
|
2019-03-14 10:30:23 +01:00
|
|
|
this.rowAndChildObjects.forEach(tableState.applyRowState.bind(tableState));
|
2019-03-13 13:53:40 +01:00
|
|
|
this.isInitialized = true;
|
2019-02-12 09:08:37 +01:00
|
|
|
})
|
|
|
|
);
|
|
|
|
},
|
|
|
|
methods: {
|
|
|
|
onVisibleColumnsChanged(visibleColumns) {
|
|
|
|
this.visibleColumns = visibleColumns;
|
|
|
|
},
|
|
|
|
onVisibleRowObjectsChanged(visibleRowObjects) {
|
|
|
|
this.visibleRowObjects = visibleRowObjects;
|
|
|
|
},
|
|
|
|
onSort(column, direction) {
|
|
|
|
function compareRows(r1, r2) {
|
|
|
|
return column.compareRows(r1, r2) * direction;
|
|
|
|
}
|
2019-03-14 10:30:23 +01:00
|
|
|
this.compareRowsCB = compareRows;
|
2019-03-13 13:53:40 +01:00
|
|
|
},
|
|
|
|
onItemClicked(clickEvent, itemId) {
|
|
|
|
if(!this.canChangeSelectionCB()) return;
|
2019-03-14 10:30:23 +01:00
|
|
|
|
2019-03-13 15:27:16 +01:00
|
|
|
if(this.isMultiToggleClick(clickEvent) && this.canMultiSelect) {
|
2019-03-13 13:53:40 +01:00
|
|
|
let slectedIdsWithoutClicked = this.selectedIds.filter(id => id !== itemId);
|
|
|
|
if (slectedIdsWithoutClicked.length < this.selectedIds.length) {
|
|
|
|
this.selectedIds = slectedIdsWithoutClicked;
|
|
|
|
} else {
|
|
|
|
this.selectedIds = [itemId, ...this.selectedIds];
|
|
|
|
}
|
2019-03-14 10:30:23 +01:00
|
|
|
} else if(this.isSelectBetweenClick(clickEvent) && this.canMultiSelect) {
|
2019-03-13 15:27:16 +01:00
|
|
|
if (this.selectedIds.length > 0) {
|
|
|
|
let betweenA = this.selectedIds[this.selectedIds.length -1];
|
|
|
|
let betweenB = itemId;
|
|
|
|
this.selectedIds = this.rowsBetween(betweenA, betweenB).map(it => it.getId());
|
|
|
|
|
|
|
|
} else {
|
|
|
|
this.selectedIds = [itemId];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
2019-03-13 13:53:40 +01:00
|
|
|
if (this.selectedIds.length === 1 && this.selectedIds[0] === itemId) {
|
|
|
|
this.selectedIds = [];
|
|
|
|
} else {
|
|
|
|
this.selectedIds = [itemId];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
2019-03-14 10:30:23 +01:00
|
|
|
isSelectBetweenClick(clickEvent) {
|
2019-03-13 15:27:16 +01:00
|
|
|
return clickEvent.shiftKey;
|
|
|
|
},
|
|
|
|
isMultiToggleClick(clickEvent) {
|
|
|
|
return clickEvent.ctrlKey ||
|
|
|
|
clickEvent.metaKey; // Mac command key
|
2019-02-12 09:08:37 +01:00
|
|
|
},
|
2019-03-14 10:30:23 +01:00
|
|
|
/**
|
|
|
|
* Get visible rows between id1 and id2
|
|
|
|
* @param {String} id1
|
|
|
|
* @param {String} id2
|
|
|
|
* @returns {Array(RowObjects)}
|
|
|
|
*/
|
2019-03-13 15:27:16 +01:00
|
|
|
rowsBetween(id1, id2) {
|
|
|
|
let hasFoundFirst = false;
|
|
|
|
let hasFoundLast = false;
|
|
|
|
return this.visibleRowObjects.filter((it) => {
|
|
|
|
if (hasFoundLast) return false;
|
|
|
|
if (!hasFoundFirst) {
|
|
|
|
hasFoundFirst = [id1, id2].includes(it.getId());
|
|
|
|
return hasFoundFirst;
|
|
|
|
}
|
|
|
|
hasFoundLast = [id1, id2].includes(it.getId());
|
|
|
|
return true;
|
|
|
|
})
|
|
|
|
}
|
2019-02-12 09:08:37 +01:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2019-03-14 10:30:23 +01:00
|
|
|
export { PillarTable, TableState }
|