Store filter/column settings in localStorage
The filter and column settings in tables are stored per project and context in the browsers localStorage. This makes the table keep the settings even if the browser is refreshed or restarted. The table emits a "componentStateChanged" event containing the tables current state (filter/column settings) which then is saved by the top level component.
This commit is contained in:
parent
f6056f4f7e
commit
465f1eb87e
@ -9,7 +9,11 @@ import { ColumnBase } from './table/columns/ColumnBase'
|
||||
import { ColumnFactoryBase } from './table/columns/ColumnFactoryBase'
|
||||
import { RowObjectsSourceBase } from './table/rows/RowObjectsSourceBase'
|
||||
import { RowBase } from './table/rows/RowObjectBase'
|
||||
import { RowFilter } from './table/filter/RowFilter'
|
||||
import { RowFilter } from './table/rows/filter/RowFilter'
|
||||
import { EnumFilter } from './table/rows/filter/EnumFilter'
|
||||
import { StatusFilter } from './table/rows/filter/StatusFilter'
|
||||
import { TextFilter } from './table/rows/filter/TextFilter'
|
||||
import { NameFilter } from './table/rows/filter/NameFilter'
|
||||
|
||||
let mixins = {
|
||||
UnitOfWorkTracker,
|
||||
@ -31,12 +35,16 @@ let table = {
|
||||
}
|
||||
},
|
||||
rows: {
|
||||
filter: {
|
||||
RowFilter,
|
||||
EnumFilter,
|
||||
StatusFilter,
|
||||
TextFilter,
|
||||
NameFilter
|
||||
},
|
||||
RowObjectsSourceBase,
|
||||
RowBase,
|
||||
},
|
||||
filter: {
|
||||
RowFilter
|
||||
},
|
||||
}
|
||||
|
||||
export { mixins, table }
|
||||
|
@ -1,6 +1,6 @@
|
||||
const TEMPLATE =`
|
||||
<div class="pillar-dropdown">
|
||||
<div class="pillar-dropdown-button"
|
||||
<div class="pillar-dropdown-button action"
|
||||
:class="buttonClasses"
|
||||
@click="toggleShowMenu"
|
||||
>
|
||||
|
@ -1,8 +1,9 @@
|
||||
import './rows/renderer/Head'
|
||||
import './rows/renderer/Row'
|
||||
import './filter/ColumnFilter'
|
||||
import './filter/RowFilter'
|
||||
import './columns/filter/ColumnFilter'
|
||||
import './rows/filter/RowFilter'
|
||||
import {UnitOfWorkTracker} from '../mixins/UnitOfWorkTracker'
|
||||
import {RowFilter} from './rows/filter/RowFilter'
|
||||
|
||||
/**
|
||||
* Table State
|
||||
@ -23,6 +24,19 @@ class TableState {
|
||||
}
|
||||
}
|
||||
|
||||
class ComponentState {
|
||||
/**
|
||||
* Serializable state of this component.
|
||||
*
|
||||
* @param {Object} rowFilter
|
||||
* @param {Object} columnFilter
|
||||
*/
|
||||
constructor(rowFilter, columnFilter) {
|
||||
this.rowFilter = rowFilter;
|
||||
this.columnFilter = columnFilter
|
||||
}
|
||||
}
|
||||
|
||||
const TEMPLATE =`
|
||||
<div class="pillar-table-container"
|
||||
:class="$options.name"
|
||||
@ -30,14 +44,19 @@ const TEMPLATE =`
|
||||
<div class="pillar-table-menu">
|
||||
<pillar-table-row-filter
|
||||
:rowObjects="sortedRowObjects"
|
||||
:config="rowFilterConfig"
|
||||
:componentState="(componentState || {}).rowFilter"
|
||||
@visibleRowObjectsChanged="onVisibleRowObjectsChanged"
|
||||
@componentStateChanged="onRowFilterStateChanged"
|
||||
/>
|
||||
<pillar-table-actions
|
||||
@item-clicked="onItemClicked"
|
||||
/>
|
||||
<pillar-table-column-filter
|
||||
:columns="columns"
|
||||
:componentState="(componentState || {}).columnFilter"
|
||||
@visibleColumnsChanged="onVisibleColumnsChanged"
|
||||
@componentStateChanged="onColumnFilterStateChanged"
|
||||
/>
|
||||
</div>
|
||||
<div class="pillar-table">
|
||||
@ -71,6 +90,7 @@ const TEMPLATE =`
|
||||
*
|
||||
* @emits isInitialized When all rows has been fetched, and are initialized.
|
||||
* @emits selectItemsChanged(selectedItems) When selected rows has changed.
|
||||
* @emits componentStateChanged(newState) When table state changed. Filtered rows, visible columns...
|
||||
*/
|
||||
let PillarTable = Vue.component('pillar-table-base', {
|
||||
template: TEMPLATE,
|
||||
@ -88,15 +108,23 @@ let PillarTable = Vue.component('pillar-table-base', {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
componentState: {
|
||||
// Instance of ComponentState
|
||||
type: Object,
|
||||
default: undefined
|
||||
}
|
||||
},
|
||||
data: function() {
|
||||
return {
|
||||
columns: [],
|
||||
visibleColumns: [],
|
||||
visibleRowObjects: [],
|
||||
rowsSource: undefined, // Override with your implementations of ColumnFactoryBase
|
||||
columnFactory: undefined, // Override with your implementations of RowSource
|
||||
rowsSource: undefined, // Override with your implementations of RowSource
|
||||
columnFactory: undefined, // Override with your implementations of ColumnFactoryBase
|
||||
rowFilterConfig: undefined,
|
||||
isInitialized: false,
|
||||
rowFilterState: (this.componentState || {}).rowFilter,
|
||||
columnFilterState: (this.componentState || {}).columnFilter,
|
||||
compareRowsCB: (row1, row2) => 0
|
||||
}
|
||||
},
|
||||
@ -120,6 +148,15 @@ let PillarTable = Vue.component('pillar-table-base', {
|
||||
selectedItems() {
|
||||
return this.rowAndChildObjects.filter(it => it.isSelected)
|
||||
.map(it => it.underlyingObject);
|
||||
},
|
||||
currentComponentState() {
|
||||
if (this.isInitialized) {
|
||||
return new ComponentState(
|
||||
this.rowFilterState,
|
||||
this.columnFilterState
|
||||
);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
@ -130,8 +167,8 @@ let PillarTable = Vue.component('pillar-table-base', {
|
||||
},
|
||||
selectedItems(newValue, oldValue) {
|
||||
// Deep compare to avoid spamming un needed events
|
||||
let hasChanged = JSON.stringify(newValue ) === JSON.stringify(oldValue);
|
||||
if (!hasChanged) {
|
||||
let hasChanged = JSON.stringify(newValue ) !== JSON.stringify(oldValue);
|
||||
if (hasChanged) {
|
||||
this.$emit('selectItemsChanged', newValue);
|
||||
}
|
||||
},
|
||||
@ -139,6 +176,15 @@ let PillarTable = Vue.component('pillar-table-base', {
|
||||
if (newValue) {
|
||||
this.$emit('isInitialized');
|
||||
}
|
||||
},
|
||||
currentComponentState(newValue, oldValue) {
|
||||
if (this.isInitialized) {
|
||||
// Deep compare to avoid spamming un needed events
|
||||
let hasChanged = JSON.stringify(newValue ) !== JSON.stringify(oldValue);
|
||||
if (hasChanged) {
|
||||
this.$emit('componentStateChanged', newValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
@ -169,9 +215,15 @@ let PillarTable = Vue.component('pillar-table-base', {
|
||||
onVisibleColumnsChanged(visibleColumns) {
|
||||
this.visibleColumns = visibleColumns;
|
||||
},
|
||||
onColumnFilterStateChanged(newComponentState) {
|
||||
this.columnFilterState = newComponentState;
|
||||
},
|
||||
onVisibleRowObjectsChanged(visibleRowObjects) {
|
||||
this.visibleRowObjects = visibleRowObjects;
|
||||
},
|
||||
onRowFilterStateChanged(newComponentState) {
|
||||
this.rowFilterState = newComponentState;
|
||||
},
|
||||
onSort(column, direction) {
|
||||
function compareRows(r1, r2) {
|
||||
return column.compareRows(r1, r2) * direction;
|
||||
@ -228,6 +280,9 @@ let PillarTable = Vue.component('pillar-table-base', {
|
||||
return true;
|
||||
})
|
||||
}
|
||||
},
|
||||
components: {
|
||||
'pillar-table-row-filter': RowFilter
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -4,13 +4,12 @@ import { CellDefault } from '../cells/renderer/CellDefault'
|
||||
* Column logic
|
||||
*/
|
||||
|
||||
let nextColumnId = 0;
|
||||
export class ColumnBase {
|
||||
constructor(displayName, columnType) {
|
||||
this._id = nextColumnId++;
|
||||
this.displayName = displayName;
|
||||
this.columnType = columnType;
|
||||
this.isMandatory = false;
|
||||
this.includedByDefault = true;
|
||||
this.isSortable = true;
|
||||
this.isHighLighted = 0;
|
||||
}
|
||||
|
@ -0,0 +1,130 @@
|
||||
import '../../../menu/DropDown'
|
||||
|
||||
const TEMPLATE =`
|
||||
<div class="pillar-table-column-filter">
|
||||
<pillar-dropdown>
|
||||
<i class="pi-cog"
|
||||
slot="button"
|
||||
title="Table Settings"/>
|
||||
|
||||
<ul class="settings-menu"
|
||||
slot="menu"
|
||||
>
|
||||
Columns:
|
||||
<li class="attract-column-select action"
|
||||
v-for="c in columnStates"
|
||||
:key="c.displayName"
|
||||
@click="toggleColumn(c)"
|
||||
>
|
||||
<input type="checkbox"
|
||||
v-model="c.isVisible"
|
||||
/>
|
||||
{{ c.displayName }}
|
||||
</li>
|
||||
</ul>
|
||||
</pillar-dropdown>
|
||||
</div>
|
||||
`;
|
||||
|
||||
class ColumnState{
|
||||
constructor() {
|
||||
this.displayName;
|
||||
this.isVisible;
|
||||
this.isMandatory;
|
||||
}
|
||||
|
||||
static createDefault(column) {
|
||||
let state = new ColumnState;
|
||||
state.displayName = column.displayName;
|
||||
state.isVisible = !!column.includedByDefault;
|
||||
state.isMandatory = !!column.isMandatory;
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
||||
class ComponentState {
|
||||
/**
|
||||
* Serializable state of this component.
|
||||
*
|
||||
* @param {Array} selected The columns that should be visible
|
||||
*/
|
||||
constructor(selected) {
|
||||
this.selected = selected;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Component to select what columns to render in the table.
|
||||
*
|
||||
* @emits visibleColumnsChanged(columns) When visible columns has changed
|
||||
* @emits componentStateChanged(newState) When column filter state changed.
|
||||
*/
|
||||
let Filter = Vue.component('pillar-table-column-filter', {
|
||||
template: TEMPLATE,
|
||||
props: {
|
||||
columns: Array, // Instances of ColumnBase
|
||||
componentState: Object, // Instance of ComponentState
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
columnStates: this.createInitialColumnStates(), // Instances of ColumnState
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
visibleColumns() {
|
||||
return this.columns.filter((candidate) => {
|
||||
return candidate.isMandatory || this.isColumnStateVisible(candidate);
|
||||
});
|
||||
},
|
||||
columnFilterState() {
|
||||
return new ComponentState(this.visibleColumns.map(it => it.displayName));
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
columns() {
|
||||
this.columnStates = this.createInitialColumnStates();
|
||||
},
|
||||
visibleColumns(visibleColumns) {
|
||||
this.$emit('visibleColumnsChanged', visibleColumns);
|
||||
},
|
||||
columnFilterState(newValue) {
|
||||
this.$emit('componentStateChanged', newValue);
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.$emit('visibleColumnsChanged', this.visibleColumns);
|
||||
},
|
||||
methods: {
|
||||
createInitialColumnStates() {
|
||||
let columnStateCB = ColumnState.createDefault;
|
||||
if (this.componentState && this.componentState.selected) {
|
||||
let selected = this.componentState.selected;
|
||||
columnStateCB = (column) => {
|
||||
let state = ColumnState.createDefault(column);
|
||||
state.isVisible = selected.includes(column.displayName);
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
||||
return this.columns.reduce((states, c) => {
|
||||
if(!c.isMandatory) {
|
||||
states.push(columnStateCB(c));
|
||||
}
|
||||
return states;
|
||||
}, []);
|
||||
},
|
||||
isColumnStateVisible(column) {
|
||||
for (let state of this.columnStates) {
|
||||
if (state.displayName === column.displayName) {
|
||||
return state.isVisible;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
toggleColumn(column) {
|
||||
column.isVisible = !column.isVisible;
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
export { Filter }
|
@ -1,91 +0,0 @@
|
||||
import '../../menu/DropDown'
|
||||
|
||||
const TEMPLATE =`
|
||||
<div class="pillar-table-column-filter">
|
||||
<pillar-dropdown>
|
||||
<i class="pi-cog"
|
||||
slot="button"
|
||||
title="Table Settings"/>
|
||||
|
||||
<ul class="settings-menu"
|
||||
slot="menu"
|
||||
>
|
||||
Columns:
|
||||
<li class="attract-column-select"
|
||||
v-for="c in columnStates"
|
||||
:key="c._id"
|
||||
>
|
||||
<input type="checkbox"
|
||||
v-model="c.isVisible"
|
||||
/>
|
||||
{{ c.displayName }}
|
||||
</li>
|
||||
</ul>
|
||||
</pillar-dropdown>
|
||||
</div>
|
||||
`;
|
||||
|
||||
class ColumnState{
|
||||
constructor(id, displayName, isVisible) {
|
||||
this.id = id;
|
||||
this.displayName = displayName;
|
||||
this.isVisible = isVisible;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Component to select what columns to render in the table.
|
||||
*
|
||||
* @emits visibleColumnsChanged(columns) When visible columns has changed
|
||||
*/
|
||||
let Filter = Vue.component('pillar-table-column-filter', {
|
||||
template: TEMPLATE,
|
||||
props: {
|
||||
columns: Array, // Instances of ColumnBase
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
columnStates: [], // Instances of ColumnState
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
visibleColumns() {
|
||||
return this.columns.filter((candidate) => {
|
||||
return candidate.isMandatory || this.isColumnStateVisible(candidate);
|
||||
});
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
columns() {
|
||||
this.columnStates = this.setColumnStates();
|
||||
},
|
||||
visibleColumns(visibleColumns) {
|
||||
this.$emit('visibleColumnsChanged', visibleColumns);
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.$emit('visibleColumnsChanged', this.visibleColumns);
|
||||
},
|
||||
methods: {
|
||||
setColumnStates() {
|
||||
return this.columns.reduce((states, c) => {
|
||||
if (!c.isMandatory) {
|
||||
states.push(
|
||||
new ColumnState(c._id, c.displayName, true)
|
||||
);
|
||||
}
|
||||
return states;
|
||||
}, [])
|
||||
},
|
||||
isColumnStateVisible(column) {
|
||||
for (let state of this.columnStates) {
|
||||
if (state.id === column._id) {
|
||||
return state.isVisible;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export { Filter }
|
@ -1,48 +0,0 @@
|
||||
const TEMPLATE =`
|
||||
<div class="pillar-table-row-filter">
|
||||
<input
|
||||
placeholder="Filter by name"
|
||||
v-model="nameQuery"
|
||||
/>
|
||||
</div>
|
||||
`;
|
||||
|
||||
/**
|
||||
* @emits visibleRowObjectsChanged(rowObjects) When the what objects to be visible has changed.
|
||||
*/
|
||||
let RowFilter = Vue.component('pillar-table-row-filter', {
|
||||
template: TEMPLATE,
|
||||
props: {
|
||||
rowObjects: Array
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
nameQuery: '',
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
nameQueryLoweCase() {
|
||||
return this.nameQuery.toLowerCase();
|
||||
},
|
||||
visibleRowObjects() {
|
||||
return this.rowObjects.filter((row) => {
|
||||
return this.filterByName(row);
|
||||
});
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
visibleRowObjects(visibleRowObjects) {
|
||||
this.$emit('visibleRowObjectsChanged', visibleRowObjects);
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.$emit('visibleRowObjectsChanged', this.visibleRowObjects);
|
||||
},
|
||||
methods: {
|
||||
filterByName(rowObject) {
|
||||
return rowObject.getName().toLowerCase().indexOf(this.nameQueryLoweCase) !== -1;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export { RowFilter }
|
@ -0,0 +1,153 @@
|
||||
const TEMPLATE =`
|
||||
<pillar-dropdown>
|
||||
<i class="pi-filter"
|
||||
slot="button"
|
||||
:class="enumButtonClasses"
|
||||
title="Filter rows"
|
||||
/>
|
||||
|
||||
<ul class="settings-menu"
|
||||
slot="menu"
|
||||
>
|
||||
<li>
|
||||
{{ label }}:
|
||||
</li>
|
||||
<li class="action"
|
||||
@click="toggleAll"
|
||||
>
|
||||
<input type="checkbox"
|
||||
:checked="includesRows"
|
||||
/> Toggle All
|
||||
</li>
|
||||
<li class="input-group-separator"/>
|
||||
<li v-for="val in enumVisibilities"
|
||||
class="action"
|
||||
:key="val.value"
|
||||
@click="toggleEnum(val.value)"
|
||||
>
|
||||
<input type="checkbox"
|
||||
v-model="enumVisibilities[val.value].isVisible"
|
||||
/> {{ val.displayName }}
|
||||
</li>
|
||||
</ul>
|
||||
</pillar-dropdown>
|
||||
`;
|
||||
|
||||
class EnumState{
|
||||
constructor(displayName, value, isVisible) {
|
||||
this.displayName = displayName;
|
||||
this.value = value;
|
||||
this.isVisible = isVisible;
|
||||
}
|
||||
}
|
||||
|
||||
class ComponentState {
|
||||
/**
|
||||
* Serializable state of this component.
|
||||
*
|
||||
* @param {Array} selected The enums that should be visible
|
||||
*/
|
||||
constructor(selected) {
|
||||
this.selected = selected;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter row objects based on enumeratable values.
|
||||
*
|
||||
* @emits visibleRowObjectsChanged(rowObjects) When the objects to be visible has changed.
|
||||
* @emits componentStateChanged(newState) When row filter state changed.
|
||||
*/
|
||||
let EnumFilter = {
|
||||
template: TEMPLATE,
|
||||
props: {
|
||||
label: String,
|
||||
availableValues: Array, // Array with valid values [{value: abc, displayName: xyz},...]
|
||||
componentState: Object, // Instance of ComponentState.
|
||||
valueExtractorCB: {
|
||||
// Callback to extract enumvalue from a rowObject
|
||||
type: Function,
|
||||
default: (rowObject) => {throw Error("Not Implemented")}
|
||||
},
|
||||
rowObjects: Array,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
enumVisibilities: this.initEnumVisibilities(),
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
visibleRowObjects() {
|
||||
return this.rowObjects.filter((row) => {
|
||||
return this.shouldBeVisible(row);
|
||||
});
|
||||
},
|
||||
includesRows() {
|
||||
for (const key in this.enumVisibilities) {
|
||||
if(!this.enumVisibilities[key].isVisible) return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
enumButtonClasses() {
|
||||
return {
|
||||
'filter-active': !this.includesRows
|
||||
}
|
||||
},
|
||||
currentComponentState() {
|
||||
let visibleEnums = [];
|
||||
for (const key in this.enumVisibilities) {
|
||||
const enumState = this.enumVisibilities[key];
|
||||
if (enumState.isVisible) {
|
||||
visibleEnums.push(enumState.value);
|
||||
}
|
||||
}
|
||||
|
||||
return new ComponentState(visibleEnums);
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
visibleRowObjects(visibleRowObjects) {
|
||||
this.$emit('visibleRowObjectsChanged', visibleRowObjects);
|
||||
},
|
||||
currentComponentState(newValue) {
|
||||
this.$emit('componentStateChanged', newValue);
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.$emit('visibleRowObjectsChanged', this.visibleRowObjects);
|
||||
},
|
||||
methods: {
|
||||
shouldBeVisible(rowObject) {
|
||||
let value = this.valueExtractorCB(rowObject);
|
||||
if (typeof this.enumVisibilities[value] === 'undefined') {
|
||||
console.warn(`RowObject ${rowObject.getId()} has an invalid ${this.label} enum: ${value}`)
|
||||
return true;
|
||||
}
|
||||
return this.enumVisibilities[value].isVisible;
|
||||
},
|
||||
initEnumVisibilities() {
|
||||
let initialValueCB = () => true;
|
||||
if (this.componentState && this.componentState.selected) {
|
||||
initialValueCB = (val) => {
|
||||
return this.componentState.selected.includes(val.value);
|
||||
};
|
||||
}
|
||||
|
||||
return this.availableValues.reduce((agg, val)=> {
|
||||
agg[val.value] = new EnumState(val.displayName, val.value, initialValueCB(val));
|
||||
return agg;
|
||||
}, {});
|
||||
},
|
||||
toggleEnum(value) {
|
||||
this.enumVisibilities[value].isVisible = !this.enumVisibilities[value].isVisible;
|
||||
},
|
||||
toggleAll() {
|
||||
let newValue = !this.includesRows;
|
||||
for (const key in this.enumVisibilities) {
|
||||
this.enumVisibilities[key].isVisible = newValue;
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
export { EnumFilter }
|
@ -0,0 +1,35 @@
|
||||
import {TextFilter} from './TextFilter'
|
||||
|
||||
const TEMPLATE =`
|
||||
<text-filter
|
||||
label="Name"
|
||||
:componentState="componentState"
|
||||
:rowObjects="rowObjects"
|
||||
:valueExtractorCB="extractName"
|
||||
@visibleRowObjectsChanged="$emit('visibleRowObjectsChanged', ...arguments)"
|
||||
@componentStateChanged="$emit('componentStateChanged', ...arguments)"
|
||||
>
|
||||
`;
|
||||
/**
|
||||
* Filter row objects based on there name.
|
||||
*
|
||||
* @emits visibleRowObjectsChanged(rowObjects) When the objects to be visible has changed.
|
||||
* @emits componentStateChanged(newState) When row filter state changed.
|
||||
*/
|
||||
let NameFilter = {
|
||||
template: TEMPLATE,
|
||||
props: {
|
||||
componentState: Object, // Instance of object that componentStateChanged emitted. To restore previous state.
|
||||
rowObjects: Array,
|
||||
},
|
||||
methods: {
|
||||
extractName(rowObject) {
|
||||
return rowObject.getName();
|
||||
},
|
||||
},
|
||||
components: {
|
||||
'text-filter': TextFilter,
|
||||
},
|
||||
};
|
||||
|
||||
export { NameFilter }
|
@ -0,0 +1,25 @@
|
||||
import {NameFilter} from './NameFilter'
|
||||
|
||||
const TEMPLATE =`
|
||||
<div class="pillar-table-row-filter">
|
||||
<name-filter
|
||||
:rowObjects="rowObjects"
|
||||
:componentState="componentState"
|
||||
@visibleRowObjectsChanged="$emit('visibleRowObjectsChanged', ...arguments)"
|
||||
@componentStateChanged="$emit('componentStateChanged', ...arguments)"
|
||||
/>
|
||||
</div>
|
||||
`;
|
||||
|
||||
let RowFilter = {
|
||||
template: TEMPLATE,
|
||||
props: {
|
||||
rowObjects: Array,
|
||||
componentState: Object
|
||||
},
|
||||
components: {
|
||||
'name-filter': NameFilter
|
||||
}
|
||||
};
|
||||
|
||||
export { RowFilter }
|
@ -0,0 +1,48 @@
|
||||
import {EnumFilter} from './EnumFilter'
|
||||
|
||||
const TEMPLATE =`
|
||||
<enum-filter
|
||||
label="Status"
|
||||
:availableValues="availableEnumValues"
|
||||
:componentState="componentState"
|
||||
:rowObjects="rowObjects"
|
||||
:valueExtractorCB="extractStatus"
|
||||
@visibleRowObjectsChanged="$emit('visibleRowObjectsChanged', ...arguments)"
|
||||
@componentStateChanged="$emit('componentStateChanged', ...arguments)"
|
||||
>
|
||||
`;
|
||||
/**
|
||||
* Filter row objects based on there status.
|
||||
*
|
||||
* @emits visibleRowObjectsChanged(rowObjects) When the objects to be visible has changed.
|
||||
* @emits componentStateChanged(newState) When row filter state changed.
|
||||
*/
|
||||
let StatusFilter = {
|
||||
template: TEMPLATE,
|
||||
props: {
|
||||
availableStatuses: Array, // Array with valid values ['abc', 'xyz']
|
||||
componentState: Object, // Instance of object that componentStateChanged emitted. To restore previous state.
|
||||
rowObjects: Array,
|
||||
},
|
||||
computed: {
|
||||
availableEnumValues() {
|
||||
let statusCopy = this.availableStatuses.concat().sort()
|
||||
return statusCopy.map(status =>{
|
||||
return {
|
||||
value: status,
|
||||
displayName: status.replace(/-|_/g, ' ') // Replace -(dash) and _(underscore) with space
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
extractStatus(rowObject) {
|
||||
return rowObject.getStatus();
|
||||
},
|
||||
},
|
||||
components: {
|
||||
'enum-filter': EnumFilter,
|
||||
},
|
||||
};
|
||||
|
||||
export { StatusFilter }
|
@ -0,0 +1,86 @@
|
||||
const TEMPLATE =`
|
||||
<input
|
||||
:class="textInputClasses"
|
||||
:placeholder="placeholderText"
|
||||
v-model="textQuery"
|
||||
/>
|
||||
`;
|
||||
|
||||
class ComponentState {
|
||||
/**
|
||||
* Serializable state of this component.
|
||||
*
|
||||
* @param {String} textQuery
|
||||
*/
|
||||
constructor(textQuery) {
|
||||
this.textQuery = textQuery;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Component to filter rowobjects by a text value
|
||||
*
|
||||
* @emits visibleRowObjectsChanged(rowObjects) When the objects to be visible has changed.
|
||||
* @emits componentStateChanged(newState) When row filter state changed. Filter query...
|
||||
*/
|
||||
let TextFilter = {
|
||||
template: TEMPLATE,
|
||||
props: {
|
||||
label: String,
|
||||
rowObjects: Array,
|
||||
componentState: {
|
||||
// Instance of ComponentState
|
||||
type: Object,
|
||||
default: undefined
|
||||
},
|
||||
valueExtractorCB: {
|
||||
// Callback to extract text to filter from a rowObject
|
||||
type: Function,
|
||||
default: (rowObject) => {throw Error("Not Implemented")}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
textQuery: (this.componentState || {}).textQuery || '',
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
textQueryLoweCase() {
|
||||
return this.textQuery.toLowerCase();
|
||||
},
|
||||
visibleRowObjects() {
|
||||
return this.rowObjects.filter((row) => {
|
||||
return this.filterByText(row);
|
||||
});
|
||||
},
|
||||
textInputClasses() {
|
||||
return {
|
||||
'filter-active': this.textQuery.length > 0
|
||||
};
|
||||
},
|
||||
currentComponentState() {
|
||||
return new ComponentState(this.textQuery);
|
||||
},
|
||||
placeholderText() {
|
||||
return `Filter by ${this.label}`;
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
visibleRowObjects(visibleRowObjects) {
|
||||
this.$emit('visibleRowObjectsChanged', visibleRowObjects);
|
||||
},
|
||||
currentComponentState(newValue) {
|
||||
this.$emit('componentStateChanged', newValue);
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.$emit('visibleRowObjectsChanged', this.visibleRowObjects);
|
||||
},
|
||||
methods: {
|
||||
filterByText(rowObject) {
|
||||
return (this.valueExtractorCB(rowObject) || '').toLowerCase().indexOf(this.textQueryLoweCase) !== -1;
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export { TextFilter }
|
@ -112,6 +112,9 @@ $thumbnail-max-height: calc(110px * (9/16))
|
||||
display: flex
|
||||
flex-direction: row
|
||||
|
||||
.action
|
||||
cursor: pointer
|
||||
|
||||
.settings-menu
|
||||
display: flex
|
||||
flex-direction: column
|
||||
@ -123,11 +126,18 @@ $thumbnail-max-height: calc(110px * (9/16))
|
||||
text-transform: capitalize
|
||||
z-index: $z-index-base + 1
|
||||
box-shadow: 0 2px 5px rgba(black, .4)
|
||||
user-select: none
|
||||
|
||||
.pillar-table-row-filter
|
||||
display: flex
|
||||
flex-direction: row
|
||||
|
||||
input.filter-active
|
||||
background-color: rgba($color-info, .50)
|
||||
|
||||
.pi-filter.filter-active
|
||||
color: $color-info
|
||||
|
||||
.pillar-table-actions
|
||||
margin-left: auto
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user