vuetify 2.5.14 → 2.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (237) hide show
  1. package/dist/json/attributes.json +196 -0
  2. package/dist/json/tags.json +88 -20
  3. package/dist/json/web-types.json +721 -141
  4. package/dist/vuetify.css +165 -5
  5. package/dist/vuetify.css.map +1 -1
  6. package/dist/vuetify.js +966 -333
  7. package/dist/vuetify.js.map +1 -1
  8. package/dist/vuetify.min.css +2 -2
  9. package/dist/vuetify.min.js +2 -2
  10. package/es5/components/VAutocomplete/VAutocomplete.js +1 -0
  11. package/es5/components/VAutocomplete/VAutocomplete.js.map +1 -1
  12. package/es5/components/VBanner/VBanner.js +3 -2
  13. package/es5/components/VBanner/VBanner.js.map +1 -1
  14. package/es5/components/VBottomNavigation/VBottomNavigation.js +10 -2
  15. package/es5/components/VBottomNavigation/VBottomNavigation.js.map +1 -1
  16. package/es5/components/VBottomSheet/VBottomSheet.js +1 -4
  17. package/es5/components/VBottomSheet/VBottomSheet.js.map +1 -1
  18. package/es5/components/VCalendar/VCalendar.js +2 -2
  19. package/es5/components/VCalendar/VCalendar.js.map +1 -1
  20. package/es5/components/VCalendar/VCalendarDaily.js +16 -8
  21. package/es5/components/VCalendar/VCalendarDaily.js.map +1 -1
  22. package/es5/components/VCalendar/VCalendarWeekly.js +14 -10
  23. package/es5/components/VCalendar/VCalendarWeekly.js.map +1 -1
  24. package/es5/components/VCalendar/mixins/calendar-with-events.js +19 -20
  25. package/es5/components/VCalendar/mixins/calendar-with-events.js.map +1 -1
  26. package/es5/components/VCalendar/mixins/mouse.js +13 -4
  27. package/es5/components/VCalendar/mixins/mouse.js.map +1 -1
  28. package/es5/components/VCombobox/VCombobox.js +1 -1
  29. package/es5/components/VCombobox/VCombobox.js.map +1 -1
  30. package/es5/components/VDataTable/MobileRow.js +2 -2
  31. package/es5/components/VDataTable/MobileRow.js.map +1 -1
  32. package/es5/components/VDataTable/Row.js +25 -9
  33. package/es5/components/VDataTable/Row.js.map +1 -1
  34. package/es5/components/VDataTable/VDataTableHeaderDesktop.js +1 -1
  35. package/es5/components/VDataTable/VDataTableHeaderDesktop.js.map +1 -1
  36. package/es5/components/VDatePicker/mixins/date-picker-table.js +13 -3
  37. package/es5/components/VDatePicker/mixins/date-picker-table.js.map +1 -1
  38. package/es5/components/VDialog/VDialog.js +4 -10
  39. package/es5/components/VDialog/VDialog.js.map +1 -1
  40. package/es5/components/VInput/VInput.js +3 -1
  41. package/es5/components/VInput/VInput.js.map +1 -1
  42. package/es5/components/VItemGroup/VItemGroup.js +8 -4
  43. package/es5/components/VItemGroup/VItemGroup.js.map +1 -1
  44. package/es5/components/VMenu/VMenu.js +0 -4
  45. package/es5/components/VMenu/VMenu.js.map +1 -1
  46. package/es5/components/VNavigationDrawer/VNavigationDrawer.js +7 -11
  47. package/es5/components/VNavigationDrawer/VNavigationDrawer.js.map +1 -1
  48. package/es5/components/VOtpInput/VOtpInput.js +377 -0
  49. package/es5/components/VOtpInput/VOtpInput.js.map +1 -0
  50. package/es5/components/VOtpInput/index.js +20 -0
  51. package/es5/components/VOtpInput/index.js.map +1 -0
  52. package/es5/components/VRadioGroup/VRadioGroup.js +1 -3
  53. package/es5/components/VRadioGroup/VRadioGroup.js.map +1 -1
  54. package/es5/components/VSelect/VSelect.js +6 -1
  55. package/es5/components/VSelect/VSelect.js.map +1 -1
  56. package/es5/components/VSlideGroup/VSlideGroup.js +12 -4
  57. package/es5/components/VSlideGroup/VSlideGroup.js.map +1 -1
  58. package/es5/components/VSnackbar/VSnackbar.js +4 -8
  59. package/es5/components/VSnackbar/VSnackbar.js.map +1 -1
  60. package/es5/components/VStepper/VStepperStep.js +12 -1
  61. package/es5/components/VStepper/VStepperStep.js.map +1 -1
  62. package/es5/components/VTabs/VTab.js +3 -2
  63. package/es5/components/VTabs/VTab.js.map +1 -1
  64. package/es5/components/VTooltip/VTooltip.js +14 -9
  65. package/es5/components/VTooltip/VTooltip.js.map +1 -1
  66. package/es5/components/VTreeview/VTreeview.js +2 -1
  67. package/es5/components/VTreeview/VTreeview.js.map +1 -1
  68. package/es5/components/VTreeview/VTreeviewNode.js +3 -1
  69. package/es5/components/VTreeview/VTreeviewNode.js.map +1 -1
  70. package/es5/components/index.js +13 -0
  71. package/es5/components/index.js.map +1 -1
  72. package/es5/directives/ripple/index.js +0 -7
  73. package/es5/directives/ripple/index.js.map +1 -1
  74. package/es5/framework.js +1 -1
  75. package/es5/locale/da.js +77 -0
  76. package/es5/locale/da.js.map +1 -0
  77. package/es5/locale/index.js +8 -0
  78. package/es5/locale/index.js.map +1 -1
  79. package/es5/locale/it.js +6 -6
  80. package/es5/locale/it.js.map +1 -1
  81. package/es5/mixins/activatable/index.js +6 -2
  82. package/es5/mixins/activatable/index.js.map +1 -1
  83. package/es5/mixins/menuable/index.js +9 -8
  84. package/es5/mixins/menuable/index.js.map +1 -1
  85. package/es5/mixins/routable/index.js +4 -7
  86. package/es5/mixins/routable/index.js.map +1 -1
  87. package/es5/services/application/index.js +1 -3
  88. package/es5/services/application/index.js.map +1 -1
  89. package/es5/util/helpers.js +13 -6
  90. package/es5/util/helpers.js.map +1 -1
  91. package/lib/components/VAutocomplete/VAutocomplete.js +1 -0
  92. package/lib/components/VAutocomplete/VAutocomplete.js.map +1 -1
  93. package/lib/components/VBanner/VBanner.js +3 -2
  94. package/lib/components/VBanner/VBanner.js.map +1 -1
  95. package/lib/components/VBottomNavigation/VBottomNavigation.js +10 -2
  96. package/lib/components/VBottomNavigation/VBottomNavigation.js.map +1 -1
  97. package/lib/components/VBottomSheet/VBottomSheet.js +1 -4
  98. package/lib/components/VBottomSheet/VBottomSheet.js.map +1 -1
  99. package/lib/components/VCalendar/VCalendar.js +2 -2
  100. package/lib/components/VCalendar/VCalendar.js.map +1 -1
  101. package/lib/components/VCalendar/VCalendarDaily.js +20 -8
  102. package/lib/components/VCalendar/VCalendarDaily.js.map +1 -1
  103. package/lib/components/VCalendar/VCalendarWeekly.js +10 -2
  104. package/lib/components/VCalendar/VCalendarWeekly.js.map +1 -1
  105. package/lib/components/VCalendar/mixins/calendar-with-events.js +6 -3
  106. package/lib/components/VCalendar/mixins/calendar-with-events.js.map +1 -1
  107. package/lib/components/VCalendar/mixins/mouse.js +9 -4
  108. package/lib/components/VCalendar/mixins/mouse.js.map +1 -1
  109. package/lib/components/VCombobox/VCombobox.js +1 -1
  110. package/lib/components/VCombobox/VCombobox.js.map +1 -1
  111. package/lib/components/VDataTable/MobileRow.js +2 -2
  112. package/lib/components/VDataTable/MobileRow.js.map +1 -1
  113. package/lib/components/VDataTable/Row.js +15 -8
  114. package/lib/components/VDataTable/Row.js.map +1 -1
  115. package/lib/components/VDataTable/VDataTableHeaderDesktop.js +1 -1
  116. package/lib/components/VDataTable/VDataTableHeaderDesktop.js.map +1 -1
  117. package/lib/components/VDatePicker/mixins/date-picker-table.js +12 -2
  118. package/lib/components/VDatePicker/mixins/date-picker-table.js.map +1 -1
  119. package/lib/components/VDialog/VDialog.js +4 -10
  120. package/lib/components/VDialog/VDialog.js.map +1 -1
  121. package/lib/components/VInput/VInput.js +2 -0
  122. package/lib/components/VInput/VInput.js.map +1 -1
  123. package/lib/components/VItemGroup/VItemGroup.js +7 -5
  124. package/lib/components/VItemGroup/VItemGroup.js.map +1 -1
  125. package/lib/components/VMenu/VMenu.js +0 -4
  126. package/lib/components/VMenu/VMenu.js.map +1 -1
  127. package/lib/components/VNavigationDrawer/VNavigationDrawer.js +3 -7
  128. package/lib/components/VNavigationDrawer/VNavigationDrawer.js.map +1 -1
  129. package/lib/components/VOtpInput/VOtpInput.js +344 -0
  130. package/lib/components/VOtpInput/VOtpInput.js.map +1 -0
  131. package/lib/components/VOtpInput/index.js +4 -0
  132. package/lib/components/VOtpInput/index.js.map +1 -0
  133. package/lib/components/VRadioGroup/VRadioGroup.js +2 -4
  134. package/lib/components/VRadioGroup/VRadioGroup.js.map +1 -1
  135. package/lib/components/VSelect/VSelect.js +6 -1
  136. package/lib/components/VSelect/VSelect.js.map +1 -1
  137. package/lib/components/VSlideGroup/VSlideGroup.js +13 -4
  138. package/lib/components/VSlideGroup/VSlideGroup.js.map +1 -1
  139. package/lib/components/VSnackbar/VSnackbar.js +4 -8
  140. package/lib/components/VSnackbar/VSnackbar.js.map +1 -1
  141. package/lib/components/VStepper/VStepperStep.js +12 -1
  142. package/lib/components/VStepper/VStepperStep.js.map +1 -1
  143. package/lib/components/VTabs/VTab.js +3 -2
  144. package/lib/components/VTabs/VTab.js.map +1 -1
  145. package/lib/components/VTooltip/VTooltip.js +14 -9
  146. package/lib/components/VTooltip/VTooltip.js.map +1 -1
  147. package/lib/components/VTreeview/VTreeview.js +2 -1
  148. package/lib/components/VTreeview/VTreeview.js.map +1 -1
  149. package/lib/components/VTreeview/VTreeviewNode.js +3 -1
  150. package/lib/components/VTreeview/VTreeviewNode.js.map +1 -1
  151. package/lib/components/index.js +1 -0
  152. package/lib/components/index.js.map +1 -1
  153. package/lib/directives/ripple/index.js +0 -7
  154. package/lib/directives/ripple/index.js.map +1 -1
  155. package/lib/framework.js +1 -1
  156. package/lib/locale/da.js +70 -0
  157. package/lib/locale/da.js.map +1 -0
  158. package/lib/locale/index.js +1 -0
  159. package/lib/locale/index.js.map +1 -1
  160. package/lib/locale/it.js +6 -6
  161. package/lib/locale/it.js.map +1 -1
  162. package/lib/mixins/activatable/index.js +6 -2
  163. package/lib/mixins/activatable/index.js.map +1 -1
  164. package/lib/mixins/menuable/index.js +9 -8
  165. package/lib/mixins/menuable/index.js.map +1 -1
  166. package/lib/mixins/routable/index.js +5 -7
  167. package/lib/mixins/routable/index.js.map +1 -1
  168. package/lib/services/application/index.js +1 -3
  169. package/lib/services/application/index.js.map +1 -1
  170. package/lib/util/helpers.js +13 -6
  171. package/lib/util/helpers.js.map +1 -1
  172. package/package.json +2 -2
  173. package/src/components/VAutocomplete/VAutocomplete.ts +2 -0
  174. package/src/components/VAutocomplete/__tests__/VAutocomplete3.spec.ts +16 -0
  175. package/src/components/VBanner/VBanner.ts +16 -10
  176. package/src/components/VBottomNavigation/VBottomNavigation.ts +14 -2
  177. package/src/components/VBottomNavigation/__tests__/VBottomNavigation.spec.ts +9 -7
  178. package/src/components/VBottomSheet/VBottomSheet.ts +1 -4
  179. package/src/components/VCalendar/VCalendar.ts +3 -2
  180. package/src/components/VCalendar/VCalendarCategory.sass +67 -0
  181. package/src/components/VCalendar/VCalendarDaily.ts +9 -8
  182. package/src/components/VCalendar/VCalendarWeekly.ts +4 -2
  183. package/src/components/VCalendar/_variables.scss +4 -0
  184. package/src/components/VCalendar/mixins/calendar-with-events.ts +4 -3
  185. package/src/components/VCalendar/mixins/mouse.ts +10 -4
  186. package/src/components/VCombobox/VCombobox.ts +1 -1
  187. package/src/components/VCombobox/__tests__/VCombobox.spec.ts +2 -2
  188. package/src/components/VDataTable/MobileRow.ts +2 -2
  189. package/src/components/VDataTable/Row.ts +23 -16
  190. package/src/components/VDataTable/VDataTableHeaderDesktop.ts +1 -1
  191. package/src/components/VDatePicker/__tests__/VDatePicker.date.spec.ts +16 -0
  192. package/src/components/VDatePicker/__tests__/VDatePicker.month.spec.ts +16 -0
  193. package/src/components/VDatePicker/mixins/date-picker-table.ts +24 -2
  194. package/src/components/VDialog/VDialog.ts +4 -10
  195. package/src/components/VInput/VInput.sass +8 -0
  196. package/src/components/VInput/VInput.ts +2 -0
  197. package/src/components/VItemGroup/VItemGroup.ts +5 -3
  198. package/src/components/VItemGroup/__tests__/VItemGroup.spec.ts +19 -2
  199. package/src/components/VMenu/VMenu.ts +0 -4
  200. package/src/components/VNavigationDrawer/VNavigationDrawer.ts +3 -6
  201. package/src/components/VOtpInput/VOtpInput.sass +37 -0
  202. package/src/components/VOtpInput/VOtpInput.ts +324 -0
  203. package/src/components/VOtpInput/__tests__/VOtpInput.spec.ts +294 -0
  204. package/src/components/VOtpInput/_variables.scss +4 -0
  205. package/src/components/VOtpInput/index.ts +4 -0
  206. package/src/components/VRadioGroup/VRadioGroup.ts +0 -4
  207. package/src/components/VSelect/VSelect.ts +4 -0
  208. package/src/components/VSlideGroup/VSlideGroup.ts +21 -2
  209. package/src/components/VSnackbar/VSnackbar.ts +3 -7
  210. package/src/components/VSnackbar/__tests__/VSnackbar.spec.ts +4 -1
  211. package/src/components/VStepper/VStepper.sass +4 -4
  212. package/src/components/VStepper/VStepperStep.ts +14 -2
  213. package/src/components/VStepper/__tests__/__snapshots__/VStepperStep.spec.ts.snap +18 -6
  214. package/src/components/VTabs/VTab.ts +10 -2
  215. package/src/components/VTextarea/VTextarea.sass +13 -0
  216. package/src/components/VTooltip/VTooltip.sass +4 -1
  217. package/src/components/VTooltip/VTooltip.ts +14 -8
  218. package/src/components/VTooltip/_variables.scss +1 -0
  219. package/src/components/VTreeview/VTreeview.ts +2 -1
  220. package/src/components/VTreeview/VTreeviewNode.ts +3 -1
  221. package/src/components/index.ts +1 -0
  222. package/src/directives/ripple/VRipple.sass +4 -1
  223. package/src/directives/ripple/index.ts +0 -7
  224. package/src/locale/da.ts +69 -0
  225. package/src/locale/index.ts +1 -0
  226. package/src/locale/it.ts +6 -6
  227. package/src/mixins/activatable/__tests__/__snapshots__/activatable.spec.ts.snap +1 -2
  228. package/src/mixins/activatable/index.ts +6 -2
  229. package/src/mixins/menuable/index.ts +8 -7
  230. package/src/mixins/routable/index.ts +3 -6
  231. package/src/services/application/index.ts +1 -1
  232. package/src/styles/settings/_variables.scss +4 -3
  233. package/src/util/__tests__/__snapshots__/helpers.spec.ts.snap +11 -0
  234. package/src/util/__tests__/helpers.spec.ts +17 -0
  235. package/src/util/helpers.ts +14 -7
  236. package/types/lib.d.ts +2 -0
  237. package/src/directives/ripple/_variables.scss +0 -6
@@ -0,0 +1,324 @@
1
+ // Styles
2
+ import '../VTextField/VTextField.sass'
3
+ import './VOtpInput.sass'
4
+
5
+ // Extensions
6
+ import VInput from '../VInput'
7
+ import VTextField from '../VTextField/VTextField'
8
+ // Directives
9
+ import ripple from '../../directives/ripple'
10
+
11
+ // Utilities
12
+ import { convertToUnit, keyCodes } from '../../util/helpers'
13
+ import { breaking } from '../../util/console'
14
+
15
+ // Types
16
+ import mixins from '../../util/mixins'
17
+ import { VNode } from 'vue'
18
+
19
+ const baseMixins = mixins(
20
+ VInput,
21
+ )
22
+
23
+ interface options extends InstanceType<typeof baseMixins> {
24
+ $refs: {
25
+ input: HTMLInputElement[]
26
+ }
27
+ }
28
+
29
+ /* @vue/component */
30
+ export default baseMixins.extend<options>().extend({
31
+ name: 'v-otp-input',
32
+
33
+ directives: {
34
+ ripple,
35
+ },
36
+
37
+ inheritAttrs: false,
38
+
39
+ props: {
40
+ length: {
41
+ type: [Number, String],
42
+ default: 6,
43
+ },
44
+ type: {
45
+ type: String,
46
+ default: 'text',
47
+ },
48
+ plain: Boolean,
49
+ },
50
+
51
+ data: () => ({
52
+ badInput: false,
53
+ initialValue: null,
54
+ isBooted: false,
55
+ otp: [] as string[],
56
+ }),
57
+
58
+ computed: {
59
+ outlined (): Boolean {
60
+ return !this.plain
61
+ },
62
+ classes (): object {
63
+ return {
64
+ ...VInput.options.computed.classes.call(this),
65
+ ...VTextField.options.computed.classes.call(this),
66
+ 'v-otp-input--plain': this.plain,
67
+ }
68
+ },
69
+ isDirty (): boolean {
70
+ return VInput.options.computed.isDirty.call(this) || this.badInput
71
+ },
72
+ },
73
+
74
+ watch: {
75
+ isFocused: 'updateValue',
76
+ value (val) {
77
+ this.lazyValue = val
78
+ this.otp = val?.split('') || []
79
+ },
80
+ },
81
+
82
+ created () {
83
+ /* istanbul ignore next */
84
+ if (this.$attrs.hasOwnProperty('browser-autocomplete')) {
85
+ breaking('browser-autocomplete', 'autocomplete', this)
86
+ }
87
+
88
+ this.otp = this.internalValue?.split('') || []
89
+ },
90
+
91
+ mounted () {
92
+ requestAnimationFrame(() => (this.isBooted = true))
93
+ },
94
+
95
+ methods: {
96
+ /** @public */
97
+ focus (e: Event, otpIdx: number) {
98
+ this.onFocus(e, otpIdx || 0)
99
+ },
100
+ genInputSlot (otpIdx: number) {
101
+ return this.$createElement('div', this.setBackgroundColor(this.backgroundColor, {
102
+ staticClass: 'v-input__slot',
103
+ style: { height: convertToUnit(this.height) },
104
+ on: {
105
+ click: () => this.onClick(otpIdx),
106
+ mousedown: (e: Event) => this.onMouseDown(e, otpIdx),
107
+ mouseup: (e: Event) => this.onMouseUp(e, otpIdx),
108
+ },
109
+ }), [this.genDefaultSlot(otpIdx)])
110
+ },
111
+ genControl (otpIdx: number) {
112
+ return this.$createElement('div', {
113
+ staticClass: 'v-input__control',
114
+ }, [
115
+ this.genInputSlot(otpIdx),
116
+ ])
117
+ },
118
+ genDefaultSlot (otpIdx: number) {
119
+ return [
120
+ this.genFieldset(),
121
+ this.genTextFieldSlot(otpIdx),
122
+ ]
123
+ },
124
+ genContent () {
125
+ return Array.from({ length: +this.length }, (_, i) => {
126
+ return this.$createElement('div', this.setTextColor(this.validationState, {
127
+ staticClass: 'v-input',
128
+ class: this.classes,
129
+ }), [this.genControl(i)])
130
+ })
131
+ },
132
+ genFieldset () {
133
+ return this.$createElement('fieldset', {
134
+ attrs: {
135
+ 'aria-hidden': true,
136
+ },
137
+ }, [this.genLegend()])
138
+ },
139
+ genLegend () {
140
+ const span = this.$createElement('span', {
141
+ domProps: { innerHTML: '&#8203;' },
142
+ })
143
+
144
+ return this.$createElement('legend', {
145
+ style: {
146
+ width: '0px',
147
+ },
148
+ }, [span])
149
+ },
150
+ genInput (otpIdx: number) {
151
+ const listeners = Object.assign({}, this.listeners$)
152
+ delete listeners.change // Change should not be bound externally
153
+
154
+ return this.$createElement('input', {
155
+ style: {},
156
+ domProps: {
157
+ value: this.otp[otpIdx],
158
+ min: this.type === 'number' ? 0 : null,
159
+ },
160
+ attrs: {
161
+ ...this.attrs$,
162
+ disabled: this.isDisabled,
163
+ readonly: this.isReadonly,
164
+ type: this.type,
165
+ id: `${this.computedId}--${otpIdx}`,
166
+ class: `otp-field-box--${otpIdx}`,
167
+ maxlength: 1,
168
+ },
169
+ on: Object.assign(listeners, {
170
+ blur: this.onBlur,
171
+ input: (e: Event) => this.onInput(e, otpIdx),
172
+ focus: (e: Event) => this.onFocus(e, otpIdx),
173
+ paste: (e: ClipboardEvent) => this.onPaste(e, otpIdx),
174
+ keydown: this.onKeyDown,
175
+ keyup: (e: KeyboardEvent) => this.onKeyUp(e, otpIdx),
176
+ }),
177
+ ref: 'input',
178
+ refInFor: true,
179
+ })
180
+ },
181
+ genTextFieldSlot (otpIdx: number): VNode {
182
+ return this.$createElement('div', {
183
+ staticClass: 'v-text-field__slot',
184
+ }, [
185
+ this.genInput(otpIdx),
186
+ ])
187
+ },
188
+ onBlur (e?: Event) {
189
+ this.isFocused = false
190
+ e && this.$nextTick(() => this.$emit('blur', e))
191
+ },
192
+ onClick (otpIdx: number) {
193
+ if (this.isFocused || this.isDisabled || !this.$refs.input[otpIdx]) return
194
+
195
+ this.onFocus(undefined, otpIdx)
196
+ },
197
+ onFocus (e?: Event, otpIdx?: number) {
198
+ e?.preventDefault()
199
+ e?.stopPropagation()
200
+ const elements = this.$refs.input as HTMLInputElement[]
201
+ const ref = this.$refs.input && elements[otpIdx || 0]
202
+ if (!ref) return
203
+
204
+ if (document.activeElement !== ref) {
205
+ ref.focus()
206
+ return ref.select()
207
+ }
208
+
209
+ if (!this.isFocused) {
210
+ this.isFocused = true
211
+ ref.select()
212
+ e && this.$emit('focus', e)
213
+ }
214
+ },
215
+ onInput (e: Event, otpIdx: number) {
216
+ const target = e.target as HTMLInputElement
217
+ const value = target.value
218
+ this.applyValue(otpIdx, target.value, () => {
219
+ this.internalValue = this.otp.join('')
220
+ })
221
+ this.badInput = target.validity && target.validity.badInput
222
+
223
+ const nextIndex = otpIdx + 1
224
+ if (value) {
225
+ if (nextIndex < +this.length) {
226
+ this.changeFocus(nextIndex)
227
+ } else {
228
+ this.clearFocus(otpIdx)
229
+ this.onCompleted()
230
+ }
231
+ }
232
+ },
233
+ clearFocus (index: number) {
234
+ const input = this.$refs.input[index] as HTMLInputElement
235
+ input.blur()
236
+ },
237
+ onKeyDown (e: KeyboardEvent) {
238
+ if (e.keyCode === keyCodes.enter) {
239
+ this.$emit('change', this.internalValue)
240
+ }
241
+
242
+ this.$emit('keydown', e)
243
+ },
244
+ onMouseDown (e: Event, otpIdx: number) {
245
+ // Prevent input from being blurred
246
+ if (e.target !== this.$refs.input[otpIdx]) {
247
+ e.preventDefault()
248
+ e.stopPropagation()
249
+ }
250
+
251
+ VInput.options.methods.onMouseDown.call(this, e)
252
+ },
253
+ onMouseUp (e: Event, otpIdx: number) {
254
+ if (this.hasMouseDown) this.focus(e, otpIdx)
255
+
256
+ VInput.options.methods.onMouseUp.call(this, e)
257
+ },
258
+ onPaste (event: ClipboardEvent, index: number) {
259
+ const maxCursor = +this.length - 1
260
+ const inputVal = event?.clipboardData?.getData('Text')
261
+ const inputDataArray = inputVal?.split('') || []
262
+ event.preventDefault()
263
+ const newOtp: string[] = [...this.otp]
264
+ for (let i = 0; i < inputDataArray.length; i++) {
265
+ const appIdx = index + i
266
+ if (appIdx > maxCursor) break
267
+ newOtp[appIdx] = inputDataArray[i].toString()
268
+ }
269
+ this.otp = newOtp
270
+ this.internalValue = this.otp.join('')
271
+ const targetFocus = Math.min(index + inputDataArray.length, maxCursor)
272
+ this.changeFocus(targetFocus)
273
+
274
+ if (newOtp.length === +this.length) { this.onCompleted(); this.clearFocus(targetFocus) }
275
+ },
276
+ applyValue (index: number, inputVal: string, next: Function) {
277
+ const newOtp: string[] = [...this.otp]
278
+ newOtp[index] = inputVal
279
+ this.otp = newOtp
280
+ next()
281
+ },
282
+ changeFocus (index: number) {
283
+ this.onFocus(undefined, index || 0)
284
+ },
285
+ updateValue (val: boolean) {
286
+ // Sets validationState from validatable
287
+ this.hasColor = val
288
+
289
+ if (val) {
290
+ this.initialValue = this.lazyValue
291
+ } else if (this.initialValue !== this.lazyValue) {
292
+ this.$emit('change', this.lazyValue)
293
+ }
294
+ },
295
+ onKeyUp (event: KeyboardEvent, index: number) {
296
+ event.preventDefault()
297
+ const eventKey = event.key
298
+ if (['Tab', 'Shift', 'Meta', 'Control', 'Alt'].includes(eventKey)) {
299
+ return
300
+ }
301
+ if (['Delete'].includes(eventKey)) {
302
+ return
303
+ }
304
+ if (eventKey === 'ArrowLeft' || (eventKey === 'Backspace' && !this.otp[index])) {
305
+ return index > 0 && this.changeFocus(index - 1)
306
+ }
307
+ if (eventKey === 'ArrowRight') {
308
+ return index + 1 < +this.length && this.changeFocus(index + 1)
309
+ }
310
+ },
311
+ onCompleted () {
312
+ const rsp = this.otp.join('')
313
+ if (rsp.length === +this.length) {
314
+ this.$emit('finish', rsp)
315
+ }
316
+ },
317
+ },
318
+ render (h): VNode {
319
+ return h('div', {
320
+ staticClass: 'v-otp-input',
321
+ class: this.themeClasses,
322
+ }, this.genContent())
323
+ },
324
+ })
@@ -0,0 +1,294 @@
1
+ import VOtpInput from '../VOtpInput'
2
+ import {
3
+ mount,
4
+ MountOptions,
5
+ Wrapper,
6
+ } from '@vue/test-utils'
7
+
8
+ describe('VOtpInput.ts', () => {
9
+ type Instance = InstanceType<typeof VOtpInput>
10
+ let mountFunction: (options?: MountOptions<Instance>) => Wrapper<Instance>
11
+ beforeEach(() => {
12
+ mountFunction = (options?: MountOptions<Instance>) => {
13
+ return mount(VOtpInput, {
14
+ // https://github.com/vuejs/vue-test-utils/issues/1130
15
+ sync: false,
16
+ ...options,
17
+ })
18
+ }
19
+ })
20
+
21
+ it('should update lazyValue when value is updated', async () => {
22
+ const wrapper = mountFunction({
23
+ propsData: {
24
+ value: 'foo',
25
+ },
26
+ })
27
+
28
+ expect(wrapper.vm.lazyValue).toBe('foo')
29
+
30
+ wrapper.setProps({ value: 'bar' })
31
+ await wrapper.vm.$nextTick()
32
+
33
+ expect(wrapper.vm.lazyValue).toBe('bar')
34
+ })
35
+
36
+ it('should trigger focus events', async () => {
37
+ // const updateValue = jest.fn()
38
+ const wrapper = mountFunction(
39
+ {
40
+ propsData: {
41
+ type: 'number',
42
+ },
43
+ }
44
+ )
45
+
46
+ wrapper.vm.focus(null, 0)
47
+
48
+ const focus = jest.fn()
49
+ wrapper.vm.$on('focus', focus)
50
+
51
+ await wrapper.vm.$nextTick()
52
+
53
+ expect(wrapper.vm.isFocused).toBe(true)
54
+ wrapper.setProps({ value: 'foo' })
55
+ await wrapper.vm.$nextTick()
56
+ wrapper.setData({ isFocused: false })
57
+ wrapper.vm.onFocus()
58
+ })
59
+
60
+ it('should fire change event when pressing enter', async () => {
61
+ const wrapper = mountFunction()
62
+ const input = wrapper.findAll('input').at(0)
63
+ const element = input.element as HTMLInputElement
64
+ const change = jest.fn()
65
+
66
+ wrapper.vm.$on('change', change)
67
+
68
+ input.trigger('focus')
69
+ element.value = 'a'
70
+ input.trigger('input')
71
+ await wrapper.vm.$nextTick()
72
+ input.trigger('focus')
73
+ await wrapper.vm.$nextTick()
74
+ input.trigger('keydown.space')
75
+ input.trigger('keydown.enter')
76
+ input.trigger('keydown.enter')
77
+
78
+ expect(change).toHaveBeenCalledTimes(2)
79
+ })
80
+
81
+ it('should call the correct event for different click locations', () => {
82
+ const onClick = jest.fn()
83
+ const onMouseDown = jest.fn()
84
+ const onMouseUp = jest.fn()
85
+ const wrapper = mountFunction({
86
+ methods: {
87
+ onClick,
88
+ onMouseDown,
89
+ onMouseUp,
90
+ },
91
+ })
92
+
93
+ const slot = wrapper.findAll('.v-input__slot').at(0)
94
+
95
+ wrapper.trigger('click')
96
+ wrapper.trigger('mousedown')
97
+ wrapper.trigger('mouseup')
98
+ slot.trigger('click')
99
+ slot.trigger('mousedown')
100
+ slot.trigger('mouseup')
101
+
102
+ expect(onClick).toHaveBeenCalledTimes(1)
103
+ expect(onMouseDown).toHaveBeenCalledTimes(1)
104
+ expect(onMouseUp).toHaveBeenCalledTimes(1)
105
+ })
106
+
107
+ it('should call the correct event for different click locations 2', () => {
108
+ const onMouseDown = jest.fn()
109
+ const wrapper = mountFunction()
110
+
111
+ const slot = wrapper.findAll('.v-input__slot').at(0)
112
+ const input = slot.find('input')
113
+
114
+ wrapper.trigger('click')
115
+ wrapper.trigger('mousedown')
116
+ slot.trigger('click')
117
+ slot.trigger('mousedown')
118
+ slot.trigger('click')
119
+ input.trigger('mousedown')
120
+
121
+ expect(onMouseDown).toHaveBeenCalledTimes(0)
122
+ })
123
+
124
+ it('should not focus input when mousedown did not originate from input', () => {
125
+ const focus = jest.fn()
126
+ const wrapper = mountFunction({
127
+ methods: { focus },
128
+ })
129
+
130
+ const input = wrapper.findAll('.v-input__slot').at(0)
131
+ const element = input.find('input').element as HTMLInputElement
132
+ input.trigger('mousedown')
133
+ input.trigger('mouseup')
134
+ input.trigger('mouseup')
135
+
136
+ expect(wrapper.vm.$refs.input[0]).toBe(element)
137
+ expect(focus).toHaveBeenCalledTimes(1)
138
+ })
139
+
140
+ it('should pass events to internal input field', () => {
141
+ const keyup = jest.fn()
142
+ const component = {
143
+ render (h) {
144
+ return h(VOtpInput, { on: { keyUp: keyup }, props: { }, attrs: {} })
145
+ },
146
+ }
147
+ const wrapper = mount(component)
148
+
149
+ const input = wrapper.findAll('input').at(0)
150
+ input.trigger('keyUp', { keyCode: 65 })
151
+
152
+ expect(keyup).toHaveBeenCalled()
153
+ })
154
+
155
+ it('should fire event when pressing keyboard defined keys', () => {
156
+ const wrapper = mountFunction()
157
+ const input = wrapper.find('input')
158
+ const element = input.element as HTMLInputElement
159
+ const change = jest.fn()
160
+
161
+ wrapper.vm.$on('change', change)
162
+
163
+ input.trigger('focus')
164
+ element.value = '1'
165
+ input.trigger('input')
166
+ input.trigger('keydown.enter')
167
+ input.trigger('keydown.enter')
168
+ const keys = ['Tab', 'Shift', 'Meta', 'Control', 'Alt', 'Delete', 'ArrowRight', 'Backspace']
169
+ keys.forEach(key => {
170
+ input.trigger('keyup', { key })
171
+ })
172
+
173
+ expect(change).toHaveBeenCalledTimes(2)
174
+ })
175
+
176
+ it('should process input when paste event', async () => {
177
+ const getData = jest.fn(() => '1337078')
178
+ const event = {
179
+ clipboardData: {
180
+ getData,
181
+ },
182
+ }
183
+
184
+ const wrapper = mountFunction({})
185
+
186
+ const input = wrapper.findAll('input').at(0)
187
+
188
+ const element = input.element as HTMLInputElement
189
+ input.trigger('focus')
190
+ await wrapper.vm.$nextTick()
191
+ expect(document.activeElement === element).toBe(true)
192
+
193
+ input.trigger('paste', event)
194
+ await wrapper.vm.$nextTick()
195
+
196
+ expect(wrapper.vm.otp).toStrictEqual('133707'.split(''))
197
+ })
198
+
199
+ it('should process input when paste event with empty event', async () => {
200
+ const event = {}
201
+
202
+ const wrapper = mountFunction({})
203
+
204
+ const input = wrapper.findAll('input').at(0)
205
+
206
+ const element = input.element as HTMLInputElement
207
+ input.trigger('focus')
208
+ await wrapper.vm.$nextTick()
209
+ expect(document.activeElement === element).toBe(true)
210
+
211
+ input.trigger('paste', event)
212
+ await wrapper.vm.$nextTick()
213
+
214
+ expect(wrapper.vm.otp).toStrictEqual(''.split(''))
215
+ })
216
+
217
+ it('should clear cursor when input typing is done', async () => {
218
+ const onFinish = jest.fn()
219
+ const clearFocus = jest.fn()
220
+ const wrapper = mountFunction({
221
+ propsData: {
222
+ value: '',
223
+ length: 2,
224
+ },
225
+ methods: {
226
+ clearFocus,
227
+ },
228
+ })
229
+
230
+ wrapper.vm.$on('finish', onFinish)
231
+
232
+ const input = wrapper.findAll('input').at(0)
233
+ const input2 = wrapper.findAll('input').at(1)
234
+ const element = input.element as HTMLInputElement
235
+ const element2 = input2.element as HTMLInputElement
236
+
237
+ input.trigger('focus')
238
+ await wrapper.vm.$nextTick()
239
+ element.value = 'a'
240
+ input.trigger('input')
241
+ await wrapper.vm.$nextTick()
242
+ expect(document.activeElement === element2).toBe(true)
243
+
244
+ await wrapper.vm.$nextTick()
245
+ element2.value = 'b'
246
+ input2.trigger('input')
247
+ await wrapper.vm.$nextTick()
248
+
249
+ const onFocus = jest.spyOn(wrapper.vm.$refs.input[0], 'focus')
250
+ expect(onFocus).toHaveBeenCalledTimes(0)
251
+ expect(onFinish).toHaveBeenCalledTimes(1)
252
+ expect(clearFocus).toHaveBeenCalledTimes(1)
253
+ })
254
+
255
+ it('should run onComplete when input cursor reached end without full OTP value', async () => {
256
+ const wrapper = mountFunction({
257
+ propsData: {
258
+ value: '',
259
+ length: 2,
260
+ },
261
+ methods: {
262
+ },
263
+ })
264
+
265
+ const input = wrapper.findAll('input').at(1)
266
+ const element = input.element as HTMLInputElement
267
+ input.trigger('focus')
268
+ await wrapper.vm.$nextTick()
269
+ input.trigger('input')
270
+ await wrapper.vm.$nextTick()
271
+ element.value = 'b'
272
+ input.trigger('input')
273
+ await wrapper.vm.$nextTick()
274
+
275
+ const onFocus = jest.spyOn(wrapper.vm.$refs.input[0], 'focus')
276
+ expect(onFocus).toHaveBeenCalledTimes(0)
277
+ })
278
+
279
+ it('should change cursor left when input focus and keyup left', async () => {
280
+ const wrapper = mountFunction()
281
+
282
+ const input = wrapper.findAll('input').at(1)
283
+ const input2 = wrapper.findAll('input').at(0)
284
+ const element = input2.element as HTMLInputElement
285
+ input.trigger('focus')
286
+ await wrapper.vm.$nextTick()
287
+ input.trigger('keyup', {
288
+ key: 'ArrowLeft',
289
+ })
290
+ await wrapper.vm.$nextTick()
291
+
292
+ expect(document.activeElement === element).toBe(true)
293
+ })
294
+ })
@@ -0,0 +1,4 @@
1
+ @import '../../styles/styles.sass';
2
+
3
+ $otp-gutter: 4px !default;
4
+ $otp-width: 24px !default;
@@ -0,0 +1,4 @@
1
+ import VOtpInput from './VOtpInput'
2
+
3
+ export { VOtpInput }
4
+ export default VOtpInput
@@ -6,15 +6,11 @@ import './VRadioGroup.sass'
6
6
  import VInput from '../VInput'
7
7
  import { BaseItemGroup } from '../VItemGroup/VItemGroup'
8
8
 
9
- // Mixins
10
- import Comparable from '../../mixins/comparable'
11
-
12
9
  // Types
13
10
  import mixins from '../../util/mixins'
14
11
  import { PropType } from 'vue'
15
12
 
16
13
  const baseMixins = mixins(
17
- Comparable,
18
14
  BaseItemGroup,
19
15
  VInput
20
16
  )
@@ -325,6 +325,10 @@ export default baseMixins.extend<options>().extend({
325
325
  for (let index = 0; index < arr.length; ++index) {
326
326
  const item = arr[index]
327
327
 
328
+ // Do not return null values if existant (#14421)
329
+ if (item == null) {
330
+ continue
331
+ }
328
332
  // Do not deduplicate headers or dividers (#12517)
329
333
  if (item.header || item.divider) {
330
334
  uniqueValues.set(item, item)
@@ -42,6 +42,12 @@ interface options extends Vue {
42
42
  }
43
43
  }
44
44
 
45
+ function bias (val: number) {
46
+ const c = 0.501
47
+ const x = Math.abs(val)
48
+ return Math.sign(val) * (x / ((1 / c - 2) * (1 - x) + 1))
49
+ }
50
+
45
51
  export function calculateUpdatedOffset (
46
52
  selectedElement: HTMLElement,
47
53
  widths: Widths,
@@ -210,7 +216,18 @@ export const BaseSlideGroup = mixins<options &
210
216
  // and need to be recalculated
211
217
  isOverflowing: 'setWidths',
212
218
  scrollOffset (val) {
213
- this.$refs.content.style.transform = `translateX(${-val}px)`
219
+ if (this.$vuetify.rtl) val = -val
220
+
221
+ let scroll =
222
+ val <= 0
223
+ ? bias(-val)
224
+ : val > this.widths.content - this.widths.wrapper
225
+ ? -(this.widths.content - this.widths.wrapper) + bias(this.widths.content - this.widths.wrapper - val)
226
+ : -val
227
+
228
+ if (this.$vuetify.rtl) scroll = -scroll
229
+
230
+ this.$refs.content.style.transform = `translateX(${scroll}px)`
214
231
  },
215
232
  },
216
233
 
@@ -462,8 +479,10 @@ export const BaseSlideGroup = mixins<options &
462
479
  wrapper: this.$refs.wrapper ? this.$refs.wrapper.clientWidth : 0,
463
480
  }, this.$vuetify.rtl, this.scrollOffset)
464
481
  },
465
- setWidths /* istanbul ignore next */ () {
482
+ setWidths () {
466
483
  window.requestAnimationFrame(() => {
484
+ if (this._isDestroyed) return
485
+
467
486
  const { content, wrapper } = this.$refs
468
487
 
469
488
  this.widths = {