quasar 2.8.3 → 2.9.0

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 (314) hide show
  1. package/dist/api/QBreadcrumbsEl.json +52 -0
  2. package/dist/api/QBtn.json +41 -6
  3. package/dist/api/QBtnDropdown.json +9 -0
  4. package/dist/api/QChip.json +9 -0
  5. package/dist/api/QCircularProgress.json +6 -0
  6. package/dist/api/QEditor.json +7 -0
  7. package/dist/api/QExpansionItem.json +59 -0
  8. package/dist/api/QItem.json +52 -0
  9. package/dist/api/QRating.json +13 -0
  10. package/dist/api/QRouteTab.json +42 -6
  11. package/dist/api/QTable.json +2 -2
  12. package/dist/icon-set/bootstrap-icons.umd.prod.js +1 -1
  13. package/dist/icon-set/eva-icons.umd.prod.js +1 -1
  14. package/dist/icon-set/fontawesome-v5-pro.umd.prod.js +1 -1
  15. package/dist/icon-set/fontawesome-v5.umd.prod.js +1 -1
  16. package/dist/icon-set/fontawesome-v6-pro.umd.prod.js +1 -1
  17. package/dist/icon-set/fontawesome-v6.umd.prod.js +1 -1
  18. package/dist/icon-set/ionicons-v4.umd.prod.js +1 -1
  19. package/dist/icon-set/line-awesome.umd.prod.js +1 -1
  20. package/dist/icon-set/material-icons-outlined.umd.prod.js +1 -1
  21. package/dist/icon-set/material-icons-round.umd.prod.js +1 -1
  22. package/dist/icon-set/material-icons-sharp.umd.prod.js +1 -1
  23. package/dist/icon-set/material-icons.umd.prod.js +1 -1
  24. package/dist/icon-set/material-symbols-outlined.umd.prod.js +1 -1
  25. package/dist/icon-set/material-symbols-rounded.umd.prod.js +1 -1
  26. package/dist/icon-set/material-symbols-sharp.umd.prod.js +1 -1
  27. package/dist/icon-set/mdi-v3.umd.prod.js +1 -1
  28. package/dist/icon-set/mdi-v4.umd.prod.js +1 -1
  29. package/dist/icon-set/mdi-v5.umd.prod.js +1 -1
  30. package/dist/icon-set/mdi-v6.umd.prod.js +1 -1
  31. package/dist/icon-set/svg-bootstrap-icons.umd.prod.js +1 -1
  32. package/dist/icon-set/svg-eva-icons.umd.prod.js +1 -1
  33. package/dist/icon-set/svg-fontawesome-v5.umd.prod.js +1 -1
  34. package/dist/icon-set/svg-fontawesome-v6.umd.prod.js +1 -1
  35. package/dist/icon-set/svg-ionicons-v4.umd.prod.js +1 -1
  36. package/dist/icon-set/svg-ionicons-v5.umd.prod.js +1 -1
  37. package/dist/icon-set/svg-ionicons-v6.umd.prod.js +1 -1
  38. package/dist/icon-set/svg-line-awesome.umd.prod.js +1 -1
  39. package/dist/icon-set/svg-material-icons-outlined.umd.prod.js +1 -1
  40. package/dist/icon-set/svg-material-icons-round.umd.prod.js +1 -1
  41. package/dist/icon-set/svg-material-icons-sharp.umd.prod.js +1 -1
  42. package/dist/icon-set/svg-material-icons.umd.prod.js +1 -1
  43. package/dist/icon-set/svg-material-symbols-outlined.umd.prod.js +1 -1
  44. package/dist/icon-set/svg-material-symbols-rounded.umd.prod.js +1 -1
  45. package/dist/icon-set/svg-material-symbols-sharp.umd.prod.js +1 -1
  46. package/dist/icon-set/svg-mdi-v6.umd.prod.js +1 -1
  47. package/dist/icon-set/svg-themify.umd.prod.js +1 -1
  48. package/dist/icon-set/themify.umd.prod.js +1 -1
  49. package/dist/lang/ar-TN.umd.prod.js +2 -2
  50. package/dist/lang/ar.umd.prod.js +2 -2
  51. package/dist/lang/az-Latn.umd.prod.js +2 -2
  52. package/dist/lang/bg.umd.prod.js +2 -2
  53. package/dist/lang/bn.umd.prod.js +2 -2
  54. package/dist/lang/ca.umd.prod.js +2 -2
  55. package/dist/lang/cs.umd.prod.js +2 -2
  56. package/dist/lang/da.umd.prod.js +2 -2
  57. package/dist/lang/de.umd.prod.js +2 -2
  58. package/dist/lang/el.umd.prod.js +2 -2
  59. package/dist/lang/en-GB.umd.prod.js +2 -2
  60. package/dist/lang/en-US.umd.prod.js +2 -2
  61. package/dist/lang/eo.umd.prod.js +2 -2
  62. package/dist/lang/es.umd.prod.js +2 -2
  63. package/dist/lang/et.umd.prod.js +2 -2
  64. package/dist/lang/eu.umd.prod.js +2 -2
  65. package/dist/lang/fa-IR.umd.prod.js +2 -2
  66. package/dist/lang/fa.umd.prod.js +2 -2
  67. package/dist/lang/fi.umd.prod.js +2 -2
  68. package/dist/lang/fr.umd.prod.js +2 -2
  69. package/dist/lang/gn.umd.prod.js +2 -2
  70. package/dist/lang/he.umd.prod.js +2 -2
  71. package/dist/lang/hr.umd.prod.js +2 -2
  72. package/dist/lang/hu.umd.prod.js +2 -2
  73. package/dist/lang/id.umd.prod.js +2 -2
  74. package/dist/lang/is.umd.prod.js +2 -2
  75. package/dist/lang/it.umd.prod.js +2 -2
  76. package/dist/lang/ja.umd.prod.js +2 -2
  77. package/dist/lang/km.umd.prod.js +2 -2
  78. package/dist/lang/ko-KR.umd.prod.js +2 -2
  79. package/dist/lang/kur-CKB.umd.prod.js +2 -2
  80. package/dist/lang/kz.umd.prod.js +2 -2
  81. package/dist/lang/lt.umd.prod.js +2 -2
  82. package/dist/lang/lu.umd.prod.js +2 -2
  83. package/dist/lang/lv.umd.prod.js +2 -2
  84. package/dist/lang/ml.umd.prod.js +2 -2
  85. package/dist/lang/mm.umd.prod.js +2 -2
  86. package/dist/lang/ms.umd.prod.js +2 -2
  87. package/dist/lang/my.umd.prod.js +2 -2
  88. package/dist/lang/nb-NO.umd.prod.js +2 -2
  89. package/dist/lang/nl.umd.prod.js +2 -2
  90. package/dist/lang/pl.umd.prod.js +2 -2
  91. package/dist/lang/pt-BR.umd.prod.js +2 -2
  92. package/dist/lang/pt.umd.prod.js +2 -2
  93. package/dist/lang/ro.umd.prod.js +2 -2
  94. package/dist/lang/ru.umd.prod.js +2 -2
  95. package/dist/lang/sk.umd.prod.js +2 -2
  96. package/dist/lang/sl.umd.prod.js +2 -2
  97. package/dist/lang/sm.umd.prod.js +2 -2
  98. package/dist/lang/sr-CYR.umd.prod.js +2 -2
  99. package/dist/lang/sr.umd.prod.js +2 -2
  100. package/dist/lang/sv.umd.prod.js +2 -2
  101. package/dist/lang/ta.umd.prod.js +2 -2
  102. package/dist/lang/th.umd.prod.js +2 -2
  103. package/dist/lang/tr.umd.prod.js +2 -2
  104. package/dist/lang/ug.umd.prod.js +2 -2
  105. package/dist/lang/uk.umd.prod.js +2 -2
  106. package/dist/lang/uz-Cyrl.umd.prod.js +2 -2
  107. package/dist/lang/uz-Latn.umd.prod.js +2 -2
  108. package/dist/lang/vi.umd.prod.js +2 -2
  109. package/dist/lang/zh-CN.umd.prod.js +2 -2
  110. package/dist/lang/zh-TW.umd.prod.js +2 -2
  111. package/dist/quasar.cjs.prod.js +2 -2
  112. package/dist/quasar.esm.js +16157 -15723
  113. package/dist/quasar.esm.prod.js +2 -2
  114. package/dist/quasar.sass +1 -1
  115. package/dist/quasar.umd.js +16290 -15854
  116. package/dist/quasar.umd.prod.js +2 -2
  117. package/dist/transforms/import-map.json +2 -0
  118. package/dist/types/api/qeditor.d.ts +17 -0
  119. package/dist/types/api/qloading.d.ts +5 -0
  120. package/dist/types/api.d.ts +2 -0
  121. package/dist/types/index.d.ts +133 -9
  122. package/dist/types/utils/is.d.ts +67 -0
  123. package/dist/types/utils/run-sequential-promises.d.ts +119 -0
  124. package/dist/types/utils.d.ts +9 -0
  125. package/dist/vetur/quasar-attributes.json +25 -1
  126. package/dist/vetur/quasar-tags.json +7 -1
  127. package/dist/web-types/web-types.json +108 -10
  128. package/lang/ar-TN.js +3 -1
  129. package/lang/ar-TN.mjs +3 -1
  130. package/lang/ar.js +3 -1
  131. package/lang/ar.mjs +3 -1
  132. package/lang/az-Latn.js +3 -1
  133. package/lang/az-Latn.mjs +3 -1
  134. package/lang/bg.js +3 -1
  135. package/lang/bg.mjs +3 -1
  136. package/lang/bn.js +3 -1
  137. package/lang/bn.mjs +3 -1
  138. package/lang/ca.js +3 -1
  139. package/lang/ca.mjs +3 -1
  140. package/lang/cs.js +3 -1
  141. package/lang/cs.mjs +3 -1
  142. package/lang/da.js +3 -1
  143. package/lang/da.mjs +3 -1
  144. package/lang/de.js +3 -1
  145. package/lang/de.mjs +3 -1
  146. package/lang/el.js +3 -1
  147. package/lang/el.mjs +3 -1
  148. package/lang/en-GB.js +3 -1
  149. package/lang/en-GB.mjs +3 -1
  150. package/lang/en-US.js +3 -1
  151. package/lang/en-US.mjs +3 -1
  152. package/lang/eo.js +3 -1
  153. package/lang/eo.mjs +3 -1
  154. package/lang/es.js +3 -1
  155. package/lang/es.mjs +3 -1
  156. package/lang/et.js +3 -1
  157. package/lang/et.mjs +3 -1
  158. package/lang/eu.js +3 -1
  159. package/lang/eu.mjs +3 -1
  160. package/lang/fa-IR.js +5 -3
  161. package/lang/fa-IR.mjs +5 -3
  162. package/lang/fa.js +5 -3
  163. package/lang/fa.mjs +5 -3
  164. package/lang/fi.js +3 -1
  165. package/lang/fi.mjs +3 -1
  166. package/lang/fr.js +3 -1
  167. package/lang/fr.mjs +3 -1
  168. package/lang/gn.js +3 -1
  169. package/lang/gn.mjs +3 -1
  170. package/lang/he.js +3 -1
  171. package/lang/he.mjs +3 -1
  172. package/lang/hr.js +3 -1
  173. package/lang/hr.mjs +3 -1
  174. package/lang/hu.js +3 -1
  175. package/lang/hu.mjs +3 -1
  176. package/lang/id.js +3 -1
  177. package/lang/id.mjs +3 -1
  178. package/lang/is.js +3 -1
  179. package/lang/is.mjs +3 -1
  180. package/lang/it.js +3 -1
  181. package/lang/it.mjs +3 -1
  182. package/lang/ja.js +3 -1
  183. package/lang/ja.mjs +3 -1
  184. package/lang/km.js +3 -1
  185. package/lang/km.mjs +3 -1
  186. package/lang/ko-KR.js +3 -1
  187. package/lang/ko-KR.mjs +3 -1
  188. package/lang/kur-CKB.js +3 -1
  189. package/lang/kur-CKB.mjs +3 -1
  190. package/lang/kz.js +3 -1
  191. package/lang/kz.mjs +3 -1
  192. package/lang/lt.js +3 -1
  193. package/lang/lt.mjs +3 -1
  194. package/lang/lu.js +3 -1
  195. package/lang/lu.mjs +3 -1
  196. package/lang/lv.js +3 -1
  197. package/lang/lv.mjs +3 -1
  198. package/lang/ml.js +3 -1
  199. package/lang/ml.mjs +3 -1
  200. package/lang/mm.js +4 -1
  201. package/lang/mm.mjs +4 -1
  202. package/lang/ms.js +3 -1
  203. package/lang/ms.mjs +3 -1
  204. package/lang/my.js +3 -1
  205. package/lang/my.mjs +3 -1
  206. package/lang/nb-NO.js +3 -1
  207. package/lang/nb-NO.mjs +3 -1
  208. package/lang/nl.js +3 -1
  209. package/lang/nl.mjs +3 -1
  210. package/lang/pl.js +3 -1
  211. package/lang/pl.mjs +3 -1
  212. package/lang/pt-BR.js +3 -1
  213. package/lang/pt-BR.mjs +3 -1
  214. package/lang/pt.js +3 -1
  215. package/lang/pt.mjs +3 -1
  216. package/lang/ro.js +3 -1
  217. package/lang/ro.mjs +3 -1
  218. package/lang/ru.js +3 -1
  219. package/lang/ru.mjs +3 -1
  220. package/lang/sk.js +3 -1
  221. package/lang/sk.mjs +3 -1
  222. package/lang/sl.js +3 -1
  223. package/lang/sl.mjs +3 -1
  224. package/lang/sm.js +3 -1
  225. package/lang/sm.mjs +3 -1
  226. package/lang/sr-CYR.js +3 -1
  227. package/lang/sr-CYR.mjs +3 -1
  228. package/lang/sr.js +3 -1
  229. package/lang/sr.mjs +3 -1
  230. package/lang/sv.js +3 -1
  231. package/lang/sv.mjs +3 -1
  232. package/lang/ta.js +3 -1
  233. package/lang/ta.mjs +3 -1
  234. package/lang/th.js +3 -1
  235. package/lang/th.mjs +3 -1
  236. package/lang/tr.js +3 -1
  237. package/lang/tr.mjs +3 -1
  238. package/lang/ug.js +5 -3
  239. package/lang/ug.mjs +5 -3
  240. package/lang/uk.js +3 -1
  241. package/lang/uk.mjs +3 -1
  242. package/lang/uz-Cyrl.js +3 -1
  243. package/lang/uz-Cyrl.mjs +3 -1
  244. package/lang/uz-Latn.js +3 -1
  245. package/lang/uz-Latn.mjs +3 -1
  246. package/lang/vi.js +3 -1
  247. package/lang/vi.mjs +3 -1
  248. package/lang/zh-CN.js +3 -1
  249. package/lang/zh-CN.mjs +3 -1
  250. package/lang/zh-TW.js +3 -1
  251. package/lang/zh-TW.mjs +3 -1
  252. package/package.json +6 -5
  253. package/src/components/breadcrumbs/QBreadcrumbsEl.js +6 -7
  254. package/src/components/breadcrumbs/QBreadcrumbsEl.json +53 -0
  255. package/src/components/btn/QBtn.js +19 -19
  256. package/src/components/btn/QBtn.json +41 -6
  257. package/src/components/btn/use-btn.js +6 -4
  258. package/src/components/btn-dropdown/QBtnDropdown.js +10 -2
  259. package/src/components/btn-dropdown/QBtnDropdown.json +8 -0
  260. package/src/components/checkbox/QCheckbox.js +1 -2
  261. package/src/components/checkbox/use-checkbox.js +2 -1
  262. package/src/components/chip/QChip.js +9 -2
  263. package/src/components/chip/QChip.json +8 -0
  264. package/src/components/chip/__tests__/QChip.spec.js +359 -46
  265. package/src/components/circular-progress/QCircularProgress.js +4 -2
  266. package/src/components/circular-progress/QCircularProgress.json +7 -0
  267. package/src/components/circular-progress/use-circular-progress.js +1 -0
  268. package/src/components/dialog/QDialog.js +7 -4
  269. package/src/components/drawer/QDrawer.js +7 -4
  270. package/src/components/editor/QEditor.json +9 -0
  271. package/src/components/expansion-item/QExpansionItem.js +37 -8
  272. package/src/components/expansion-item/QExpansionItem.json +67 -0
  273. package/src/components/fab/QFab.js +20 -1
  274. package/src/components/form/QForm.js +35 -40
  275. package/src/components/form/QFormChildMixin.js +3 -1
  276. package/src/components/item/QItem.js +4 -5
  277. package/src/components/item/QItem.json +53 -0
  278. package/src/components/menu/QMenu.js +5 -5
  279. package/src/components/menu/__tests__/QMenu.spec.js +7 -0
  280. package/src/components/page/QPage.js +1 -1
  281. package/src/components/popup-edit/QPopupEdit.js +2 -5
  282. package/src/components/radio/QRadio.js +3 -3
  283. package/src/components/rating/QRating.js +48 -10
  284. package/src/components/rating/QRating.json +11 -0
  285. package/src/components/select/QSelect.js +1 -4
  286. package/src/components/stepper/QStep.js +5 -3
  287. package/src/components/table/QTable.js +4 -6
  288. package/src/components/table/QTable.json +2 -2
  289. package/src/components/tabs/QRouteTab.js +6 -4
  290. package/src/components/tabs/QRouteTab.json +42 -6
  291. package/src/components/tabs/QTabs.js +188 -107
  292. package/src/components/tabs/use-tab.js +62 -38
  293. package/src/components/time/QTime.js +2 -2
  294. package/src/components/tooltip/QTooltip.js +7 -13
  295. package/src/components/tree/QTree.js +1 -1
  296. package/src/components/uploader/uploader-core.js +2 -3
  297. package/src/composables/private/__tests__/use-model-toggle.spec.js +2 -0
  298. package/src/composables/private/__tests__/use-transition.spec.js +4 -0
  299. package/src/composables/private/use-file.js +1 -1
  300. package/src/composables/private/use-router-link.js +80 -43
  301. package/src/composables/private/use-tick.js +15 -9
  302. package/src/composables/private/use-timeout.js +20 -7
  303. package/src/composables/private/use-validate.js +7 -13
  304. package/src/composables/use-form-child.js +6 -4
  305. package/src/directives/TouchPan.js +1 -1
  306. package/src/directives/TouchRepeat.js +1 -1
  307. package/src/directives/TouchSwipe.js +1 -1
  308. package/src/utils/EventBus.js +64 -0
  309. package/src/utils/extend.js +19 -19
  310. package/src/utils/private/inject-obj-prop.js +2 -0
  311. package/src/utils/private/rtl.js +10 -7
  312. package/src/utils/private/vm.js +4 -0
  313. package/src/utils/run-sequential-promises.js +115 -0
  314. package/src/utils.js +4 -0
@@ -1,4 +1,4 @@
1
- import { h, ref, computed, watch, nextTick, onBeforeUnmount, onActivated, onDeactivated, getCurrentInstance, provide } from 'vue'
1
+ import { h, ref, computed, watch, onBeforeUnmount, onActivated, onDeactivated, getCurrentInstance, provide } from 'vue'
2
2
 
3
3
  import QIcon from '../icon/QIcon.js'
4
4
  import QResizeObserver from '../resize-observer/QResizeObserver.js'
@@ -21,7 +21,6 @@ function getIndicatorClass (color, top, vertical) {
21
21
  }
22
22
 
23
23
  const alignValues = [ 'left', 'center', 'right', 'justify' ]
24
- const emptyFn = () => {}
25
24
 
26
25
  export default createComponent({
27
26
  name: 'QTabs',
@@ -67,12 +66,15 @@ export default createComponent({
67
66
  },
68
67
 
69
68
  setup (props, { slots, emit }) {
70
- const vm = getCurrentInstance()
71
- const { proxy: { $q } } = vm
69
+ const { proxy } = getCurrentInstance()
70
+ const { $q } = proxy
72
71
 
73
72
  const { registerTick: registerScrollTick } = useTick()
73
+ const { registerTick: registerUpdateArrowsTick } = useTick()
74
+ const { registerTick: registerAnimateTick } = useTick()
75
+
74
76
  const { registerTimeout: registerFocusTimeout, removeTimeout: removeFocusTimeout } = useTimeout()
75
- const { registerTimeout } = useTimeout()
77
+ const { registerTimeout: registerScrollToTabTimeout, removeTimeout: removeScrollToTabTimeout } = useTimeout()
76
78
 
77
79
  const rootRef = ref(null)
78
80
  const contentRef = ref(null)
@@ -87,10 +89,11 @@ export default createComponent({
87
89
  $q.platform.is.desktop === true || props.mobileArrows === true
88
90
  )
89
91
 
90
- const tabList = []
92
+ const tabDataList = []
93
+ const tabDataListLen = ref(0)
91
94
  const hasFocus = ref(false)
92
95
 
93
- let localFromRoute = false, animateTimer, scrollTimer, unwatchRoute
96
+ let animateTimer, scrollTimer, unwatchRoute
94
97
  let localUpdateArrows = arrowsEnabled.value === true
95
98
  ? updateArrowsFn
96
99
  : noop
@@ -109,6 +112,19 @@ export default createComponent({
109
112
  noCaps: props.noCaps
110
113
  }))
111
114
 
115
+ const hasActiveTab = computed(() => {
116
+ const len = tabDataListLen.value
117
+ const val = currentModel.value
118
+
119
+ for (let i = 0; i < len; i++) {
120
+ if (tabDataList[ i ].name.value === val) {
121
+ return true
122
+ }
123
+ }
124
+
125
+ return false
126
+ })
127
+
112
128
  const alignClass = computed(() => {
113
129
  const align = scrollable.value === true
114
130
  ? 'left'
@@ -150,7 +166,7 @@ export default createComponent({
150
166
  })
151
167
 
152
168
  watch(() => props.outsideArrows, () => {
153
- nextTick(recalculateScroll())
169
+ recalculateScroll()
154
170
  })
155
171
 
156
172
  watch(arrowsEnabled, v => {
@@ -158,12 +174,15 @@ export default createComponent({
158
174
  ? updateArrowsFn
159
175
  : noop
160
176
 
161
- nextTick(recalculateScroll())
177
+ recalculateScroll()
162
178
  })
163
179
 
164
180
  function updateModel ({ name, setCurrent, skipEmit, fromRoute }) {
165
181
  if (currentModel.value !== name) {
166
- skipEmit !== true && emit('update:modelValue', name)
182
+ if (skipEmit !== true && props[ 'onUpdate:modelValue' ] !== void 0) {
183
+ emit('update:modelValue', name)
184
+ }
185
+
167
186
  if (
168
187
  setCurrent === true
169
188
  || props[ 'onUpdate:modelValue' ] === void 0
@@ -172,20 +191,14 @@ export default createComponent({
172
191
  currentModel.value = name
173
192
  }
174
193
  }
175
-
176
- if (fromRoute !== void 0) {
177
- localFromRoute = fromRoute
178
- }
179
194
  }
180
195
 
181
196
  function recalculateScroll () {
182
197
  registerScrollTick(() => {
183
- if (vm.isDeactivated !== true && vm.isUnmounted !== true) {
184
- updateContainer({
185
- width: rootRef.value.offsetWidth,
186
- height: rootRef.value.offsetHeight
187
- })
188
- }
198
+ updateContainer({
199
+ width: rootRef.value.offsetWidth,
200
+ height: rootRef.value.offsetHeight
201
+ })
189
202
  })
190
203
  }
191
204
 
@@ -207,27 +220,21 @@ export default createComponent({
207
220
  ),
208
221
  scroll = size > 0 && scrollSize > size // when there is no tab, in Chrome, size === 0 and scrollSize === 1
209
222
 
210
- if (scrollable.value !== scroll) {
211
- scrollable.value = scroll
212
- }
223
+ scrollable.value = scroll
213
224
 
214
225
  // Arrows need to be updated even if the scroll status was already true
215
- scroll === true && nextTick(localUpdateArrows)
226
+ scroll === true && registerUpdateArrowsTick(localUpdateArrows)
216
227
 
217
- const localJustify = size < parseInt(props.breakpoint, 10)
218
-
219
- if (justify.value !== localJustify) {
220
- justify.value = localJustify
221
- }
228
+ justify.value = size < parseInt(props.breakpoint, 10)
222
229
  }
223
230
 
224
231
  function animate (oldName, newName) {
225
232
  const
226
233
  oldTab = oldName !== void 0 && oldName !== null && oldName !== ''
227
- ? tabList.find(tab => tab.name.value === oldName)
234
+ ? tabDataList.find(tab => tab.name.value === oldName)
228
235
  : null,
229
236
  newTab = newName !== void 0 && newName !== null && newName !== ''
230
- ? tabList.find(tab => tab.name.value === newName)
237
+ ? tabDataList.find(tab => tab.name.value === newName)
231
238
  : null
232
239
 
233
240
  if (oldTab && newTab) {
@@ -251,7 +258,7 @@ export default createComponent({
251
258
  : `translate3d(${ oldPos.left - newPos.left }px,0,0) scale3d(${ newPos.width ? oldPos.width / newPos.width : 1 },1,1)`
252
259
 
253
260
  // allow scope updates to kick in (QRouteTab needs more time)
254
- nextTick(() => {
261
+ registerAnimateTick(() => {
255
262
  animateTimer = setTimeout(() => {
256
263
  newEl.style.transition = 'transform .25s cubic-bezier(.4, 0, .2, 1)'
257
264
  newEl.style.transform = 'none'
@@ -306,8 +313,6 @@ export default createComponent({
306
313
 
307
314
  function animScrollTo (value) {
308
315
  stopAnimScroll()
309
- scrollTowards(value)
310
-
311
316
  scrollTimer = setInterval(() => {
312
317
  if (scrollTowards(value) === true) {
313
318
  stopAnimScroll()
@@ -338,10 +343,12 @@ export default createComponent({
338
343
 
339
344
  if (keyCode === 36) { // Home
340
345
  scrollToTabEl(tabs[ 0 ])
346
+ tabs[ 0 ].focus()
341
347
  return true
342
348
  }
343
349
  if (keyCode === 35) { // End
344
350
  scrollToTabEl(tabs[ len - 1 ])
351
+ tabs[ len - 1 ].focus()
345
352
  return true
346
353
  }
347
354
 
@@ -407,77 +414,113 @@ export default createComponent({
407
414
  return done
408
415
  }
409
416
 
410
- function getRouteList () {
411
- return tabList.filter(tab => tab.routerProps !== void 0 && tab.routerProps.hasRouterLink.value === true)
417
+ function hasQueryIncluded (targetQuery, matchingQuery) {
418
+ for (const key in targetQuery) {
419
+ if (targetQuery[ key ] !== matchingQuery[ key ]) {
420
+ return false
421
+ }
422
+ }
423
+
424
+ return true
412
425
  }
413
426
 
414
427
  // do not use directly; use verifyRouteModel() instead
415
428
  function updateActiveRoute () {
416
- let name = null, wasActive = localFromRoute
429
+ let name = null, bestScore = { matchedLen: 0, queryDiff: 9999, hrefLen: 0 }
417
430
 
418
- const
419
- best = { matchedLen: 0, hrefLen: 0, exact: false, found: false },
420
- { hash } = vm.proxy.$route,
421
- model = currentModel.value
422
-
423
- let wasItActive = wasActive === true
424
- ? emptyFn
425
- : tab => {
426
- if (model === tab.name.value) {
427
- wasActive = true
428
- wasItActive = emptyFn
429
- }
430
- }
431
+ const list = tabDataList.filter(tab => tab.routeData !== void 0 && tab.routeData.hasRouterLink.value === true)
432
+ const { hash: currentHash, query: currentQuery } = proxy.$route
433
+ const currentQueryLen = Object.keys(currentQuery).length
431
434
 
432
- const tabList = getRouteList()
435
+ // Vue Router does not keep account of hash & query when matching
436
+ // so we're doing this as well
433
437
 
434
- for (const tab of tabList) {
435
- const exact = tab.routerProps.exact.value === true
438
+ for (const tab of list) {
439
+ const exact = tab.routeData.exact.value === true
436
440
 
437
- if (
438
- tab.routerProps[ exact === true ? 'linkIsExactActive' : 'linkIsActive' ].value !== true
439
- || (best.exact === true && exact !== true)
440
- ) {
441
- wasItActive(tab)
441
+ if (tab.routeData[ exact === true ? 'linkIsExactActive' : 'linkIsActive' ].value !== true) {
442
+ // it cannot match anything as it's not active nor exact-active
442
443
  continue
443
444
  }
444
445
 
445
- const
446
- linkRoute = tab.routerProps.linkRoute.value,
447
- tabHash = linkRoute.hash
446
+ const { hash, query, matched, href } = tab.routeData.resolvedLink.value
447
+ const queryLen = Object.keys(query).length
448
448
 
449
- // Vue Router does not match the hash too, even if link is set to "exact"
450
449
  if (exact === true) {
451
- if (hash === tabHash) {
452
- name = tab.name.value
453
- break
450
+ if (hash !== currentHash) {
451
+ // it's set to exact but it doesn't matches the hash
452
+ continue
454
453
  }
455
- else if (hash !== '' && tabHash !== '') {
456
- wasItActive(tab)
454
+
455
+ if (
456
+ queryLen !== currentQueryLen
457
+ || hasQueryIncluded(currentQuery, query) === false
458
+ ) {
459
+ // it's set to exact but it doesn't matches the query
457
460
  continue
458
461
  }
462
+
463
+ // yey, we found the perfect match (route + hash + query)
464
+ name = tab.name.value
465
+ break
459
466
  }
460
467
 
461
- const
462
- matchedLen = linkRoute.matched.length,
463
- hrefLen = linkRoute.href.length - tabHash.length
468
+ if (hash !== '' && hash !== currentHash) {
469
+ // it has hash and it doesn't matches
470
+ continue
471
+ }
464
472
 
465
473
  if (
466
- matchedLen === best.matchedLen
467
- ? hrefLen > best.hrefLen
468
- : matchedLen > best.matchedLen
474
+ queryLen !== 0
475
+ && hasQueryIncluded(query, currentQuery) === false
469
476
  ) {
477
+ // it has query and it doesn't includes the current one
478
+ continue
479
+ }
480
+
481
+ const newScore = {
482
+ matchedLen: matched.length,
483
+ queryDiff: currentQueryLen - queryLen,
484
+ hrefLen: href.length - hash.length
485
+ }
486
+
487
+ if (newScore.matchedLen > bestScore.matchedLen) {
488
+ // it matches more routes so it's more specific so we set it as current champion
470
489
  name = tab.name.value
471
- Object.assign(best, { matchedLen, hrefLen, exact })
490
+ bestScore = newScore
491
+ continue
492
+ }
493
+ else if (newScore.matchedLen !== bestScore.matchedLen) {
494
+ // it matches less routes than the current champion so we discard it
472
495
  continue
473
496
  }
474
497
 
475
- wasItActive(tab)
498
+ if (newScore.queryDiff < bestScore.queryDiff) {
499
+ // query is closer to the current one so we set it as current champion
500
+ name = tab.name.value
501
+ bestScore = newScore
502
+ }
503
+ else if (newScore.queryDiff !== bestScore.queryDiff) {
504
+ // it matches less routes than the current champion so we discard it
505
+ continue
506
+ }
507
+
508
+ if (newScore.hrefLen > bestScore.hrefLen) {
509
+ // href is lengthier so it's more specific so we set it as current champion
510
+ name = tab.name.value
511
+ bestScore = newScore
512
+ }
476
513
  }
477
514
 
478
- if (wasActive === true || name !== null) {
479
- updateModel({ name, setCurrent: true, fromRoute: true })
515
+ if (
516
+ name === null
517
+ && tabDataList.some(tab => tab.routeData === void 0 && tab.name.value === currentModel.value) === true
518
+ ) {
519
+ // we shouldn't interfere if non-route tab is active
520
+ return
480
521
  }
522
+
523
+ updateModel({ name, setCurrent: true })
481
524
  }
482
525
 
483
526
  function onFocusin (e) {
@@ -495,6 +538,7 @@ export default createComponent({
495
538
  // (it might be other elements focused, like additional QBtn)
496
539
  if (tab && rootRef.value.contains(tab) === true) {
497
540
  hasFocus.value = true
541
+ scrollable.value === true && scrollToTabEl(tab)
498
542
  }
499
543
  }
500
544
  }
@@ -504,22 +548,52 @@ export default createComponent({
504
548
  }
505
549
 
506
550
  function verifyRouteModel () {
507
- if ($tabs.avoidRouteWatcher !== true) {
508
- registerTimeout(updateActiveRoute)
551
+ if ($tabs.avoidRouteWatcher === false) {
552
+ registerScrollToTabTimeout(updateActiveRoute)
553
+ }
554
+ else {
555
+ removeScrollToTabTimeout()
509
556
  }
510
557
  }
511
558
 
512
- function registerTab (getTab) {
513
- tabList.push(getTab)
559
+ function watchRoute () {
560
+ if (unwatchRoute === void 0) {
561
+ const unwatch = watch(() => proxy.$route.fullPath, verifyRouteModel)
562
+ unwatchRoute = () => {
563
+ unwatch()
564
+ unwatchRoute = void 0
565
+ }
566
+ }
567
+ }
514
568
 
515
- const routeList = getRouteList()
569
+ function registerTab (tabData) {
570
+ tabDataList.push(tabData)
571
+ tabDataListLen.value++
516
572
 
517
- if (routeList.length > 0) {
518
- if (unwatchRoute === void 0) {
519
- unwatchRoute = watch(() => vm.proxy.$route, verifyRouteModel)
520
- }
573
+ recalculateScroll()
521
574
 
522
- verifyRouteModel()
575
+ // if it's a QTab
576
+ if (tabData.routeData === void 0) {
577
+ // we should position to the currently active tab (if any)
578
+ registerScrollToTabTimeout(() => {
579
+ if (scrollable.value === true) {
580
+ const value = currentModel.value
581
+ const newTab = value !== void 0 && value !== null && value !== ''
582
+ ? tabDataList.find(tab => tab.name.value === value)
583
+ : null
584
+
585
+ newTab && scrollToTabEl(newTab.rootRef.value)
586
+ }
587
+ })
588
+ }
589
+ // else if it's a QRouteTab with a valid link
590
+ else {
591
+ // start watching route
592
+ watchRoute()
593
+
594
+ if (tabData.routeData.hasRouterLink.value === true) {
595
+ verifyRouteModel()
596
+ }
523
597
  }
524
598
  }
525
599
 
@@ -531,16 +605,18 @@ export default createComponent({
531
605
  * always check the existing list again and infer the changes.
532
606
  */
533
607
  function unregisterTab (tabData) {
534
- tabList.splice(tabList.indexOf(tabData), 1)
608
+ tabDataList.splice(tabDataList.indexOf(tabData), 1)
609
+ tabDataListLen.value--
535
610
 
536
- if (unwatchRoute !== void 0) {
537
- const routeList = getRouteList()
611
+ recalculateScroll()
538
612
 
539
- if (routeList.length === 0) {
613
+ if (unwatchRoute !== void 0 && tabData.routeData !== void 0) {
614
+ // unwatch route if we don't have any QRouteTabs left
615
+ if (tabDataList.every(tab => tab.routeData === void 0) === true) {
540
616
  unwatchRoute()
541
- unwatchRoute = void 0
542
617
  }
543
618
 
619
+ // then update model
544
620
  verifyRouteModel()
545
621
  }
546
622
  }
@@ -549,33 +625,38 @@ export default createComponent({
549
625
  currentModel,
550
626
  tabProps,
551
627
  hasFocus,
628
+ hasActiveTab,
552
629
 
553
630
  registerTab,
554
631
  unregisterTab,
555
632
 
556
633
  verifyRouteModel,
557
634
  updateModel,
558
- recalculateScroll,
559
635
  onKbdNavigate,
560
636
 
561
- avoidRouteWatcher: false
637
+ avoidRouteWatcher: false // false | string (uid)
562
638
  }
563
639
 
564
640
  provide(tabsKey, $tabs)
565
641
 
566
- onBeforeUnmount(() => {
642
+ function cleanup () {
567
643
  clearTimeout(animateTimer)
644
+ stopAnimScroll()
568
645
  unwatchRoute !== void 0 && unwatchRoute()
569
- })
646
+ }
647
+
648
+ let hadRouteWatcher
570
649
 
571
- let shouldActivate = false
650
+ onBeforeUnmount(cleanup)
572
651
 
573
652
  onDeactivated(() => {
574
- shouldActivate = true
653
+ hadRouteWatcher = unwatchRoute !== void 0
654
+ cleanup()
575
655
  })
576
656
 
577
657
  onActivated(() => {
578
- shouldActivate === true && recalculateScroll()
658
+ hadRouteWatcher === true && watchRoute()
659
+ recalculateScroll()
579
660
  })
580
661
 
581
662
  return () => {
@@ -594,22 +675,22 @@ export default createComponent({
594
675
  class: 'q-tabs__arrow q-tabs__arrow--left absolute q-tab__icon'
595
676
  + (leftArrow.value === true ? '' : ' q-tabs__arrow--faded'),
596
677
  name: props.leftIcon || $q.iconSet.tabs[ props.vertical === true ? 'up' : 'left' ],
597
- onMousedown: scrollToStart,
678
+ onMousedownPassive: scrollToStart,
598
679
  onTouchstartPassive: scrollToStart,
599
- onMouseup: stopAnimScroll,
600
- onMouseleave: stopAnimScroll,
601
- onTouchend: stopAnimScroll
680
+ onMouseupPassive: stopAnimScroll,
681
+ onMouseleavePassive: stopAnimScroll,
682
+ onTouchendPassive: stopAnimScroll
602
683
  }),
603
684
 
604
685
  h(QIcon, {
605
686
  class: 'q-tabs__arrow q-tabs__arrow--right absolute q-tab__icon'
606
687
  + (rightArrow.value === true ? '' : ' q-tabs__arrow--faded'),
607
688
  name: props.rightIcon || $q.iconSet.tabs[ props.vertical === true ? 'down' : 'right' ],
608
- onMousedown: scrollToEnd,
689
+ onMousedownPassive: scrollToEnd,
609
690
  onTouchstartPassive: scrollToEnd,
610
- onMouseup: stopAnimScroll,
611
- onMouseleave: stopAnimScroll,
612
- onTouchend: stopAnimScroll
691
+ onMouseupPassive: stopAnimScroll,
692
+ onMouseleavePassive: stopAnimScroll,
693
+ onTouchendPassive: stopAnimScroll
613
694
  })
614
695
  )
615
696
 
@@ -8,8 +8,10 @@ import { hMergeSlot } from '../../utils/private/render.js'
8
8
  import { isKeyCode, shouldIgnoreKey } from '../../utils/private/key-composition.js'
9
9
  import { tabsKey } from '../../utils/private/symbols.js'
10
10
  import { stopAndPrevent } from '../../utils/event.js'
11
+ import uid from '../../utils/uid.js'
12
+ import { isDeepEqual } from '../../utils/is.js'
11
13
 
12
- let uid = 0
14
+ let id = 0
13
15
 
14
16
  export const useTabEmits = [ 'click', 'keydown' ]
15
17
 
@@ -22,7 +24,7 @@ export const useTabProps = {
22
24
 
23
25
  name: {
24
26
  type: [ Number, String ],
25
- default: () => `t_${ uid++ }`
27
+ default: () => `t_${ id++ }`
26
28
  },
27
29
 
28
30
  noCaps: Boolean,
@@ -38,7 +40,7 @@ export const useTabProps = {
38
40
  }
39
41
  }
40
42
 
41
- export default function (props, slots, emit, routerProps) {
43
+ export default function (props, slots, emit, routeData) {
42
44
  const $tabs = inject(tabsKey, () => {
43
45
  console.error('QTab/QRouteTab component needs to be child of QTabs')
44
46
  })
@@ -75,7 +77,7 @@ export default function (props, slots, emit, routerProps) {
75
77
  + (props.icon && props.label && $tabs.tabProps.value.inlineLabel === false ? ' q-tab--full' : '')
76
78
  + (props.noCaps === true || $tabs.tabProps.value.noCaps === true ? ' q-tab--no-caps' : '')
77
79
  + (props.disable === true ? ' disabled' : ' q-focusable q-hoverable cursor-pointer')
78
- + (routerProps !== void 0 && routerProps.linkClass.value !== '' ? ` ${ routerProps.linkClass.value }` : '')
80
+ + (routeData !== void 0 ? routeData.linkClass.value : '')
79
81
  )
80
82
 
81
83
  const innerClass = computed(() =>
@@ -85,53 +87,77 @@ export default function (props, slots, emit, routerProps) {
85
87
  )
86
88
 
87
89
  const tabIndex = computed(() => (
88
- props.disable === true || $tabs.hasFocus.value === true
90
+ (
91
+ props.disable === true
92
+ || $tabs.hasFocus.value === true
93
+ || (isActive.value === false && $tabs.hasActiveTab.value === true)
94
+ )
89
95
  ? -1
90
96
  : props.tabindex || 0
91
97
  ))
92
98
 
93
99
  function onClick (e, keyboard) {
94
- keyboard !== true && blurTargetRef.value !== null && blurTargetRef.value.focus()
95
-
96
- if (props.disable !== true) {
97
- let go
100
+ if (keyboard !== true && blurTargetRef.value !== null) {
101
+ blurTargetRef.value.focus()
102
+ }
98
103
 
99
- if (routerProps !== void 0) {
100
- if (routerProps.hasRouterLink.value === true) {
101
- go = () => {
102
- e.__qNavigate = true
103
- $tabs.avoidRouteWatcher = true
104
+ if (props.disable === true) {
105
+ // we should hinder native navigation though
106
+ if (routeData !== void 0 && routeData.hasRouterLink.value === true) {
107
+ stopAndPrevent(e)
108
+ }
109
+ return
110
+ }
104
111
 
105
- const res = routerProps.navigateToRouterLink(e)
112
+ // do we have a QTab?
113
+ if (routeData === void 0) {
114
+ $tabs.updateModel({ name: props.name })
115
+ emit('click', e)
116
+ return
117
+ }
106
118
 
107
- if (res === false) {
119
+ if (routeData.hasRouterLink.value === true) {
120
+ const go = (opts = {}) => {
121
+ // if requiring to go to another route, then we
122
+ // let the QTabs route watcher do its job,
123
+ // otherwise directly select this
124
+ let hardError
125
+ const reqId = opts.to === void 0 || isDeepEqual(opts.to, props.to) === true
126
+ ? ($tabs.avoidRouteWatcher = uid())
127
+ : null
128
+
129
+ return routeData.navigateToRouterLink(e, { ...opts, returnRouterError: true })
130
+ .catch(err => { hardError = err })
131
+ .then(softError => {
132
+ if (reqId === $tabs.avoidRouteWatcher) {
108
133
  $tabs.avoidRouteWatcher = false
134
+
135
+ // if we don't have any hard errors or any soft errors, except for
136
+ // when navigating to the same route (on all other soft errors,
137
+ // like when navigation was aborted in a nav guard, we don't activate this tab)
138
+ if (
139
+ hardError === void 0 && (
140
+ softError === void 0
141
+ || softError.message.startsWith('Avoided redundant navigation') === true
142
+ )
143
+ ) {
144
+ $tabs.updateModel({ name: props.name })
145
+ }
109
146
  }
110
- else {
111
- res.then(err => {
112
- $tabs.avoidRouteWatcher = false
113
-
114
- if (err === void 0) {
115
- $tabs.updateModel({ name: props.name, fromRoute: true })
116
- }
117
- })
147
+
148
+ if (opts.returnRouterError === true) {
149
+ return hardError !== void 0 ? Promise.reject(hardError) : softError
118
150
  }
119
- }
120
- }
121
- else {
122
- emit('click', e)
123
- return
124
- }
125
- }
126
- else {
127
- go = () => {
128
- $tabs.updateModel({ name: props.name, fromRoute: false })
129
- }
151
+ })
130
152
  }
131
153
 
132
154
  emit('click', e, go)
133
155
  e.defaultPrevented !== true && go()
156
+
157
+ return
134
158
  }
159
+
160
+ emit('click', e)
135
161
  }
136
162
 
137
163
  function onKeydown (e) {
@@ -205,17 +231,15 @@ export default function (props, slots, emit, routerProps) {
205
231
  name: computed(() => props.name),
206
232
  rootRef,
207
233
  tabIndicatorRef,
208
- routerProps
234
+ routeData
209
235
  }
210
236
 
211
237
  onBeforeUnmount(() => {
212
238
  $tabs.unregisterTab(tabData)
213
- $tabs.recalculateScroll()
214
239
  })
215
240
 
216
241
  onMounted(() => {
217
242
  $tabs.registerTab(tabData)
218
- $tabs.recalculateScroll()
219
243
  })
220
244
 
221
245
  function renderTab (tag, customData) {
@@ -12,6 +12,7 @@ import { hSlot } from '../../utils/private/render.js'
12
12
  import { formatDate, __splitDate } from '../../utils/date.js'
13
13
  import { position } from '../../utils/event.js'
14
14
  import { pad } from '../../utils/format.js'
15
+ import { vmIsDestroyed } from '../../utils/private/vm.js'
15
16
 
16
17
  function getViewByModel (model, withSeconds) {
17
18
  if (model.hour !== null) {
@@ -386,8 +387,7 @@ export default createComponent({
386
387
  }
387
388
 
388
389
  function shouldAbortInteraction () {
389
- return vm.isDeactivated === true
390
- || vm.isUnmounted === true
390
+ return vmIsDestroyed(vm) === true
391
391
  // if we have limited options, can we actually set any?
392
392
  || (
393
393
  viewValidOptions.value !== null