vuetifyjs / vuetify
1 1
// Styles
2 1
import './VItemGroup.sass'
3

4
// Mixins
5
import Groupable from '../../mixins/groupable'
6 1
import Proxyable from '../../mixins/proxyable'
7 1
import Themeable from '../../mixins/themeable'
8

9
// Utilities
10 1
import mixins from '../../util/mixins'
11 1
import { consoleWarn } from '../../util/console'
12

13
// Types
14
import { VNode } from 'vue/types'
15

16
export type GroupableInstance = InstanceType<typeof Groupable> & {
17
  id?: string
18
  to?: any
19
  value?: any
20
 }
21

22 1
export const BaseItemGroup = mixins(
23
  Proxyable,
24
  Themeable
25
).extend({
26
  name: 'base-item-group',
27

28
  props: {
29
    activeClass: {
30
      type: String,
31
      default: 'v-item--active',
32
    },
33
    mandatory: Boolean,
34
    max: {
35
      type: [Number, String],
36
      default: null,
37
    },
38
    multiple: Boolean,
39
  },
40

41 1
  data () {
42 1
    return {
43
      // As long as a value is defined, show it
44
      // Otherwise, check if multiple
45
      // to determine which default to provide
46
      internalLazyValue: this.value !== undefined
47 1
        ? this.value
48 1
        : this.multiple ? [] : undefined,
49
      items: [] as GroupableInstance[],
50
    }
51
  },
52

53
  computed: {
54 1
    classes (): Record<string, boolean> {
55 1
      return {
56
        'v-item-group': true,
57
        ...this.themeClasses,
58
      }
59
    },
60 1
    selectedIndex (): number {
61 1
      return (this.selectedItem && this.items.indexOf(this.selectedItem)) || -1
62
    },
63 1
    selectedItem (): GroupableInstance | undefined {
64 1
      if (this.multiple) return undefined
65

66 1
      return this.selectedItems[0]
67
    },
68 1
    selectedItems (): GroupableInstance[] {
69 1
      return this.items.filter((item, index) => {
70 1
        return this.toggleMethod(this.getValue(item, index))
71
      })
72
    },
73 1
    selectedValues (): any[] {
74 1
      if (this.internalValue == null) return []
75

76 1
      return Array.isArray(this.internalValue)
77 1
        ? this.internalValue
78 1
        : [this.internalValue]
79
    },
80 1
    toggleMethod (): (v: any) => boolean {
81 1
      if (!this.multiple) {
82 1
        return (v: any) => this.internalValue === v
83
      }
84

85 1
      const internalValue = this.internalValue
86 1
      if (Array.isArray(internalValue)) {
87 1
        return (v: any) => internalValue.includes(v)
88
      }
89

90 1
      return () => false
91
    },
92
  },
93

94
  watch: {
95
    internalValue: 'updateItemsState',
96
    items: 'updateItemsState',
97
  },
98

99 1
  created () {
100 1
    if (this.multiple && !Array.isArray(this.internalValue)) {
101 1
      consoleWarn('Model must be bound to an array if the multiple property is true.', this)
102
    }
103
  },
104

105
  methods: {
106

107 1
    genData (): object {
108 1
      return {
109
        class: this.classes,
110
      }
111
    },
112 1
    getValue (item: GroupableInstance, i: number): unknown {
113 1
      return item.value == null || item.value === ''
114 1
        ? i
115 1
        : item.value
116
    },
117 1
    onClick (item: GroupableInstance) {
118 1
      this.updateInternalValue(
119
        this.getValue(item, this.items.indexOf(item))
120
      )
121
    },
122 1
    register (item: GroupableInstance) {
123 1
      const index = this.items.push(item) - 1
124

125 1
      item.$on('change', () => this.onClick(item))
126

127
      // If no value provided and mandatory,
128
      // assign first registered item
129 1
      if (this.mandatory && !this.selectedValues.length) {
130 1
        this.updateMandatory()
131
      }
132

133 1
      this.updateItem(item, index)
134
    },
135 1
    unregister (item: GroupableInstance) {
136 1
      if (this._isDestroyed) return
137

138 1
      const index = this.items.indexOf(item)
139 1
      const value = this.getValue(item, index)
140

141 1
      this.items.splice(index, 1)
142

143 1
      const valueIndex = this.selectedValues.indexOf(value)
144

145
      // Items is not selected, do nothing
146 1
      if (valueIndex < 0) return
147

148
      // If not mandatory, use regular update process
149 1
      if (!this.mandatory) {
150 1
        return this.updateInternalValue(value)
151
      }
152

153
      // Remove the value
154 1
      if (this.multiple && Array.isArray(this.internalValue)) {
155 1
        this.internalValue = this.internalValue.filter(v => v !== value)
156
      } else {
157 1
        this.internalValue = undefined
158
      }
159

160
      // If mandatory and we have no selection
161
      // add the last item as value
162
      /* istanbul ignore else */
163 1
      if (!this.selectedItems.length) {
164 1
        this.updateMandatory(true)
165
      }
166
    },
167 1
    updateItem (item: GroupableInstance, index: number) {
168 1
      const value = this.getValue(item, index)
169

170 1
      item.isActive = this.toggleMethod(value)
171
    },
172
    // https://github.com/vuetifyjs/vuetify/issues/5352
173 1
    updateItemsState () {
174 1
      this.$nextTick(() => {
175 1
        if (this.mandatory &&
176 1
          !this.selectedItems.length
177
        ) {
178 1
          return this.updateMandatory()
179
        }
180

181
        // TODO: Make this smarter so it
182
        // doesn't have to iterate every
183
        // child in an update
184 1
        this.items.forEach(this.updateItem)
185
      })
186
    },
187 1
    updateInternalValue (value: any) {
188 1
      this.multiple
189 1
        ? this.updateMultiple(value)
190 1
        : this.updateSingle(value)
191
    },
192 1
    updateMandatory (last?: boolean) {
193 1
      if (!this.items.length) return
194

195 1
      const items = this.items.slice()
196

197 1
      if (last) items.reverse()
198

199 1
      const item = items.find(item => !item.disabled)
200

201
      // If no tabs are available
202
      // aborts mandatory value
203 1
      if (!item) return
204

205 1
      const index = this.items.indexOf(item)
206

207 1
      this.updateInternalValue(
208
        this.getValue(item, index)
209
      )
210
    },
211 1
    updateMultiple (value: any) {
212 1
      const defaultValue = Array.isArray(this.internalValue)
213 1
        ? this.internalValue
214 1
        : []
215 1
      const internalValue = defaultValue.slice()
216 1
      const index = internalValue.findIndex(val => val === value)
217

218 1
      if (
219 1
        this.mandatory &&
220
        // Item already exists
221 1
        index > -1 &&
222
        // value would be reduced below min
223 1
        internalValue.length - 1 < 1
224 1
      ) return
225

226 1
      if (
227
        // Max is set
228 1
        this.max != null &&
229
        // Item doesn't exist
230 1
        index < 0 &&
231
        // value would be increased above max
232 1
        internalValue.length + 1 > this.max
233 1
      ) return
234

235 1
      index > -1
236 1
        ? internalValue.splice(index, 1)
237 1
        : internalValue.push(value)
238

239 1
      this.internalValue = internalValue
240
    },
241 1
    updateSingle (value: any) {
242 1
      const isSame = value === this.internalValue
243

244 1
      if (this.mandatory && isSame) return
245

246 1
      this.internalValue = isSame ? undefined : value
247
    },
248
  },
249

250 1
  render (h): VNode {
251 1
    return h('div', this.genData(), this.$slots.default)
252
  },
253
})
254

255 1
export default BaseItemGroup.extend({
256
  name: 'v-item-group',
257

258 1
  provide (): object {
259 1
    return {
260
      itemGroup: this,
261
    }
262
  },
263
})

Read our documentation on viewing source code .

Loading