UNPKG

vuetify

Version:

Vue.js 2 Semantic Component Framework

235 lines (198 loc) 5.84 kB
// Styles import '../../stylus/components/_item-group.styl' // Mixins import Groupable from '../../mixins/groupable' import Proxyable from '../../mixins/proxyable' import Themeable from '../../mixins/themeable' // Utilities import mixins from '../../util/mixins' import { consoleWarn } from '../../util/console' // Types import { VNode } from 'vue/types' type GroupableInstance = InstanceType<typeof Groupable> & { value?: any } export const BaseItemGroup = mixins( Proxyable, Themeable ).extend({ name: 'base-item-group', props: { activeClass: { type: String, default: 'v-item--active' }, mandatory: Boolean, max: { type: [Number, String], default: null }, multiple: Boolean }, data () { return { // As long as a value is defined, show it // Otherwise, check if multiple // to determine which default to provide internalLazyValue: this.value !== undefined ? this.value : this.multiple ? [] : undefined, items: [] as GroupableInstance[] } }, computed: { classes (): Record<string, boolean> { return { ...this.themeClasses } }, selectedItems (): GroupableInstance[] { return this.items.filter((item, index) => { return this.toggleMethod(this.getValue(item, index)) }) }, selectedValues (): any[] { return Array.isArray(this.internalValue) ? this.internalValue : [this.internalValue] }, toggleMethod (): (v: any) => boolean { if (!this.multiple) { return (v: any) => this.internalValue === v } const internalValue = this.internalValue if (Array.isArray(internalValue)) { return (v: any) => internalValue.includes(v) } return () => false } }, watch: { internalValue () { // https://github.com/vuetifyjs/vuetify/issues/5352 this.$nextTick(this.updateItemsState) } }, created () { if (this.multiple && !Array.isArray(this.internalValue)) { consoleWarn('Model must be bound to an array if the multiple property is true.', this) } }, methods: { getValue (item: GroupableInstance, i: number): unknown { return item.value == null || item.value === '' ? i : item.value }, onClick (item: GroupableInstance, index: number) { this.updateInternalValue( this.getValue(item, index) ) }, register (item: GroupableInstance) { const index = this.items.push(item) - 1 item.$on('change', () => this.onClick(item, index)) // If no value provided and mandatory, // assign first registered item if (this.mandatory && this.internalLazyValue == null) { this.updateMandatory() } this.updateItem(item, index) }, unregister (item: GroupableInstance) { if (this._isDestroyed) return const index = this.items.indexOf(item) const value = this.getValue(item, index) this.items.splice(index, 1) const valueIndex = this.selectedValues.indexOf(value) // Items is not selected, do nothing if (valueIndex < 0) return // If not mandatory, use regular update process if (!this.mandatory) { return this.updateInternalValue(value) } // Remove the value if (this.multiple && Array.isArray(this.internalValue)) { this.internalValue = this.internalValue.filter(v => v !== value) } else { this.internalValue = undefined } // If mandatory and we have no selection // add the last item as value /* istanbul ignore else */ if (!this.selectedItems.length) { this.updateMandatory(true) } }, updateItem (item: GroupableInstance, index: number) { const value = this.getValue(item, index) item.isActive = this.toggleMethod(value) }, updateItemsState () { if (this.mandatory && !this.selectedItems.length ) { return this.updateMandatory() } // TODO: Make this smarter so it // doesn't have to iterate every // child in an update this.items.forEach(this.updateItem) }, updateInternalValue (value: any) { this.multiple ? this.updateMultiple(value) : this.updateSingle(value) }, updateMandatory (last?: boolean) { if (!this.items.length) return const index = last ? this.items.length - 1 : 0 this.updateInternalValue( this.getValue(this.items[index], index) ) }, updateMultiple (value: any) { const defaultValue = Array.isArray(this.internalValue) ? this.internalValue : [] const internalValue = defaultValue.slice() const index = internalValue.findIndex(val => val === value) if ( this.mandatory && // Item already exists index > -1 && // value would be reduced below min internalValue.length - 1 < 1 ) return if ( // Max is set this.max != null && // Item doesn't exist index < 0 && // value woudl be increased above max internalValue.length + 1 > this.max ) return index > -1 ? internalValue.splice(index, 1) : internalValue.push(value) this.internalValue = internalValue }, updateSingle (value: any) { const isSame = value === this.internalValue if (this.mandatory && isSame) return this.internalValue = isSame ? undefined : value } }, render (h): VNode { return h('div', { staticClass: 'v-item-group', class: this.classes }, this.$slots.default) } }) export default BaseItemGroup.extend({ name: 'v-item-group', provide (): object { return { itemGroup: this } } })