@jgjergj/vuetify-toolkit
Version:
Vuetify.js Additional components set
397 lines (395 loc) • 13.1 kB
text/typescript
import Vue, { PropType, VNode } from 'vue'
import {
VBtnA,
VIconA,
VSelectA,
VTextFieldA,
VMenuA,
VDividerA,
VCardA,
VCardActionsA,
VRowA
} from '../../shims-vuetify'
import VFilterValueList from './VFilterValueList'
import { SelectableValue, TableHeader, FilterCondition, GetItem } from '../VAdvDataTable/utils/AdvTableUtils'
export default Vue.extend({
name: 'v-table-filter',
props: {
header: Object as PropType<TableHeader>,
getItemValues: {
type: Function,
default: Function as PropType<GetItem>
},
filterIcon: {
type: String,
default: undefined
},
filterActiveIcon: {
type: String,
default: undefined
},
filterIconColor: {
type: String,
default: undefined
},
filterActiveIconColor: {
type: String,
default: undefined
},
filterSelectionIcon: {
type: String,
default: '$unfold'
},
dark: {
type: Boolean,
default: false
},
dense: {
type: Boolean,
default: false
}
},
computed: {
values (): string[]| number[] {
if (this.valueDataType === 'text') {
return [...new Set((this.getItemValues as GetItem)(this.header.value) as string[])]
} else {
return [...new Set((this.getItemValues as GetItem)(this.header.value) as number[])]
}
},
conditions (): FilterCondition[] {
const result: FilterCondition[] = []
if (!this.header.datatype || this.header.datatype === 'string') {
result.push({ id: 'contains', text: 'contains' })
result.push({ id: 'startsWith', text: 'starts with' })
result.push({ id: 'endsWith', text: 'ends with' })
} else if (this.header.datatype === 'date' || this.header.datatype === 'number') {
result.push({ id: 'lessThen', text: 'less than' })
result.push({ id: 'moreThan', text: 'more than' })
result.push({ id: 'between', text: 'between' })
result.push({ id: 'equals', text: 'equals' })
}
return result
},
defaultCondition (): FilterCondition {
if (this.header.datatype === 'date' || this.header.datatype === 'number') {
return { id: 'equals', text: 'equals' }
}
return { id: 'contains', text: 'contains' }
},
valueDataType (): string {
if (!this.header.datatype || this.header.datatype === 'string') {
return 'text'
} else if (this.header.datatype === 'date') {
return 'text'
} else if (this.header.datatype === 'number') {
return 'number'
}
return 'text'
},
filteredValues (): string[]|number[] {
const conditions = this.selectedCondition ? this.selectedCondition : this.defaultCondition
if (this.header.datatype) {
if (this.header.datatype === 'number') {
const vals = this.values as number[]
if (conditions && conditions.id === 'lessThen' && this.filterText && this.filterText !== '') {
return vals.filter((v: number) => v < Number(this.filterText))
} else if (conditions && conditions.id === 'moreThan' && this.filterText && this.filterText !== '') {
return vals.filter((v: number) => v > Number(this.filterText))
} else if (conditions && conditions.id === 'equals' && this.filterText && this.filterText !== '') {
return vals.filter((v: number) => v === Number(this.filterText))
} else if (conditions && conditions.id === 'between' && this.filterText && this.filterText !== '' &&
this.filterTextMax && this.filterTextMax !== '') {
return vals.filter((v: number) => v >= Number(this.filterText) && v <= Number(this.filterTextMax))
} else return this.values
} else if (this.header.datatype === 'date') {
const vals = this.values as string[]
if (conditions && conditions.id === 'lessThen' && this.filterText && this.filterText !== '') {
return vals.filter((v: string) => v < this.filterText)
} else if (conditions && conditions.id === 'moreThan' && this.filterText && this.filterText !== '') {
return vals.filter((v: string) => v > this.filterText)
} else if (conditions && conditions.id === 'equals' && this.filterText && this.filterText !== '') {
return vals.filter((v: string) => v === this.filterText)
} else if (conditions && conditions.id === 'between' && this.filterText && this.filterText !== '' &&
this.filterTextMax && this.filterTextMax !== '') {
return vals.filter((v: string) => v >= this.filterText && v <= this.filterTextMax)
} else return this.values
}
}
if (this.filterText === '' || !this.filterText || !conditions) return this.values
const vals = this.values as string[]
if (conditions.id === 'startsWith') {
return vals.filter(v => v.toLocaleLowerCase().startsWith(this.filterText.toLocaleLowerCase()))
} else if (conditions.id === 'endsWith') {
return vals.filter(v => v.toLocaleLowerCase().endsWith(this.filterText.toLocaleLowerCase()))
}
return vals.filter((v: string) => v.toString().toLocaleLowerCase().indexOf(this.filterText.toString().toLocaleLowerCase()) >= 0)
},
filteredValuesSelected: {
get: function ():SelectableValue[] {
const vals: SelectableValue[] = []
if (this.valueDataType === 'number') {
const filteredVals = this.filteredValues as number[]
filteredVals.map(v => vals.push({ text: v.toString(), selected: true }))
} else {
const filteredVals = this.filteredValues as string[]
filteredVals.map(v => {
vals.push({ text: v, selected: true })
})
}
return vals
},
set: function () {
}
},
resultValues (): string[] {
const vals: string[] = []
this.filteredValuesSelected.map((v:SelectableValue) => {
if (this.unSelectedValues.indexOf(v.text.toString()) === -1) {
vals.push(v.text.toString())
}
})
return vals
}
},
data: () => ({
isActive: false,
isMenuActive: false,
dataHeaders: undefined,
filterText: '',
filterTextMax: '',
selectedCondition: undefined as FilterCondition|undefined,
unSelectedValues: [] as string[]
}),
methods: {
setCondition (e: FilterCondition) {
this.selectedCondition = e
},
changeSelection (item: SelectableValue) {
item.selected = !item.selected
if (!item.selected) {
this.unSelectedValues.push(item.text)
} else {
const i = this.unSelectedValues.indexOf(item.text)
if (i > -1) {
this.unSelectedValues.splice(i, 1)
}
}
},
clearFilter () {
this.isMenuActive = false
this.isActive = false
this.unSelectedValues = []
this.filterText = ''
this.filterTextMax = ''
this.selectedCondition = undefined
this.$emit('clear-filter', this.header)
},
invertSelection () {
this.$nextTick(() => {
const result = this.filteredValuesSelected.map<SelectableValue>((item: SelectableValue) => {
item.selected = !item.selected
if (!item.selected) {
this.unSelectedValues.push(item.text)
}
return item
})
this.filteredValuesSelected = result.slice()
})
},
valueFilterChange (e?: string) {
this.filterText = e || ''
},
valueFilterChangeMax (e?: string) {
this.filterTextMax = e || ''
},
genActivator (listeners: any): VNode {
return this.$createElement(VBtnA, {
props: {
icon: true
},
slot: 'activator',
on: {
click: (e: any) => {
e.stopPropagation()
this.isMenuActive = !this.isMenuActive
},
listeners
}
}, [
this.genIcon()
])
},
genIcon (): VNode {
return this.$createElement(VIconA, {
props: {
color: this.isActive ? this.filterActiveIconColor : this.filterIconColor
}
}, [this.filterIcon])
},
genDualConditionsField (): VNode {
return this.$createElement(VRowA, {
staticClass: 'v-dual-conditions-field-row'
}, [
this.$createElement(VTextFieldA, {
props: {
type: this.valueDataType,
label: 'min value',
clearable: true,
outlined: true,
dark: this.dark,
dense: true
},
on: {
input: (e: string|undefined) => this.valueFilterChange(e)
}
}),
this.$createElement(VTextFieldA, {
props: {
type: this.valueDataType,
label: 'max value',
clearable: true,
outlined: true,
dark: this.dark,
dense: true
},
on: {
input: (e: string|undefined) => this.valueFilterChangeMax(e)
}
})
])
},
genMonoConditionsField (): VNode {
return this.$createElement(VTextFieldA, {
props: {
type: this.valueDataType,
label: 'Filter by value',
clearable: true,
outlined: true,
dark: this.dark,
dense: true,
appendIcon: this.$props.filterSelectionIcon
},
style: {
'padding-left': '16px',
'padding-right': '16px',
'padding-top': '16px'
},
on: {
input: (e: string|undefined) => this.valueFilterChange(e),
'click:append': () => this.invertSelection()
}
})
},
genConditionsField (): VNode {
if (this.selectedCondition && this.selectedCondition.id === 'between') {
return this.genDualConditionsField()
}
return this.genMonoConditionsField()
},
genMenuContent (): VNode|undefined {
if (!this.header || !this.header.value) return
return this.$createElement(VCardA, {
props: {
dark: this.dark,
dense: this.dense
}
},
[
this.$createElement(VSelectA, {
props: {
label: 'Filter by condition',
items: this.conditions,
itemKey: 'id',
itemText: 'text',
returnObject: true,
value: this.selectedCondition ? this.selectedCondition : this.defaultCondition,
outlined: true,
dense: true,
dark: this.dark
},
style: {
'padding-left': '16px',
'padding-right': '16px',
'padding-top': '16px'
},
on: {
change: (e: FilterCondition) => this.setCondition(e)
}
}),
this.genConditionsField(),
this.$createElement(VFilterValueList, {
props: {
values: this.filteredValuesSelected
},
on: {
'change-value-selection': (e: any, payload: SelectableValue) => {
this.changeSelection(payload)
}
}
}),
this.$createElement(VDividerA, {}),
this.$createElement(VCardActionsA, {
style: {
'justify-content': 'flex-end'
},
props: {
dark: this.dark,
dense: this.dense
}
}, [
this.$createElement(VBtnA, {
props: {
text: true,
dark: this.dark,
dense: this.dense
},
domProps: {
innerHTML: 'Clear'
},
on: {
click: () => this.clearFilter()
}
}),
this.$createElement(VBtnA, {
props: {
text: true,
dark: this.dark,
dense: this.dense
},
domProps: {
innerHTML: 'OK'
},
on: {
click: () => this.changeFilter()
}
})
])
])
},
changeFilter () {
this.isMenuActive = false
this.isActive = true
this.$emit('filter-change', this.resultValues)
}
},
render (): VNode {
const self = this
return this.$createElement(VMenuA, {
props: {
closeOnContentClick: false,
value: this.isMenuActive,
dark: this.dark,
dense: this.dense
},
slot: 'header.data-table-settings',
scopedSlots: {
'activator' (on) {
return self.genActivator(on)
}
}
}, [
this.genMenuContent()
])
}
})