UNPKG

@jgjergj/vuetify-toolkit

Version:
397 lines (395 loc) 13.1 kB
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() ]) } })