quasar 1.20.2 → 1.21.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 (287) hide show
  1. package/dist/api/QBreadcrumbsEl.json +53 -0
  2. package/dist/api/QBtn.json +42 -5
  3. package/dist/api/QBtnDropdown.json +1 -1
  4. package/dist/api/QChip.json +1 -1
  5. package/dist/api/QDialog.json +6 -0
  6. package/dist/api/QExpansionItem.json +1 -1
  7. package/dist/api/QItem.json +53 -0
  8. package/dist/api/QRating.json +13 -0
  9. package/dist/api/QRouteTab.json +6 -2
  10. package/dist/babel-transforms/imports.js +2 -12
  11. package/dist/icon-set/bootstrap-icons.umd.min.js +1 -1
  12. package/dist/icon-set/eva-icons.umd.min.js +1 -1
  13. package/dist/icon-set/fontawesome-v5-pro.umd.min.js +1 -1
  14. package/dist/icon-set/fontawesome-v5.umd.min.js +1 -1
  15. package/dist/icon-set/fontawesome-v6-pro.umd.min.js +1 -1
  16. package/dist/icon-set/fontawesome-v6.umd.min.js +1 -1
  17. package/dist/icon-set/ionicons-v4.umd.min.js +1 -1
  18. package/dist/icon-set/line-awesome.umd.min.js +1 -1
  19. package/dist/icon-set/material-icons-outlined.umd.min.js +1 -1
  20. package/dist/icon-set/material-icons-round.umd.min.js +1 -1
  21. package/dist/icon-set/material-icons-sharp.umd.min.js +1 -1
  22. package/dist/icon-set/material-icons.umd.min.js +1 -1
  23. package/dist/icon-set/material-symbols-outlined.umd.min.js +1 -1
  24. package/dist/icon-set/material-symbols-rounded.umd.min.js +1 -1
  25. package/dist/icon-set/material-symbols-sharp.umd.min.js +1 -1
  26. package/dist/icon-set/mdi-v3.umd.min.js +1 -1
  27. package/dist/icon-set/mdi-v4.umd.min.js +1 -1
  28. package/dist/icon-set/mdi-v5.umd.min.js +1 -1
  29. package/dist/icon-set/mdi-v6.umd.min.js +1 -1
  30. package/dist/icon-set/svg-bootstrap-icons.umd.min.js +1 -1
  31. package/dist/icon-set/svg-eva-icons.umd.min.js +1 -1
  32. package/dist/icon-set/svg-fontawesome-v5.umd.min.js +1 -1
  33. package/dist/icon-set/svg-fontawesome-v6.umd.min.js +2 -2
  34. package/dist/icon-set/svg-ionicons-v4.umd.min.js +1 -1
  35. package/dist/icon-set/svg-ionicons-v5.umd.min.js +1 -1
  36. package/dist/icon-set/svg-ionicons-v6.umd.min.js +1 -1
  37. package/dist/icon-set/svg-line-awesome.umd.min.js +1 -1
  38. package/dist/icon-set/svg-material-icons-outlined.umd.min.js +1 -1
  39. package/dist/icon-set/svg-material-icons-round.umd.min.js +1 -1
  40. package/dist/icon-set/svg-material-icons-sharp.umd.min.js +1 -1
  41. package/dist/icon-set/svg-material-icons.umd.min.js +1 -1
  42. package/dist/icon-set/svg-material-symbols-outlined.umd.min.js +2 -2
  43. package/dist/icon-set/svg-material-symbols-rounded.umd.min.js +2 -2
  44. package/dist/icon-set/svg-material-symbols-sharp.umd.min.js +2 -2
  45. package/dist/icon-set/svg-mdi-v4.umd.min.js +1 -1
  46. package/dist/icon-set/svg-mdi-v5.umd.min.js +1 -1
  47. package/dist/icon-set/svg-mdi-v6.umd.min.js +1 -1
  48. package/dist/icon-set/svg-themify.umd.min.js +1 -1
  49. package/dist/icon-set/themify.umd.min.js +1 -1
  50. package/dist/lang/ar.umd.min.js +1 -1
  51. package/dist/lang/az-latn.umd.min.js +1 -1
  52. package/dist/lang/bg.umd.min.js +1 -1
  53. package/dist/lang/ca.umd.min.js +1 -1
  54. package/dist/lang/cs.umd.min.js +1 -1
  55. package/dist/lang/da.umd.min.js +1 -1
  56. package/dist/lang/de.umd.min.js +1 -1
  57. package/dist/lang/el.umd.min.js +1 -1
  58. package/dist/lang/en-gb.umd.min.js +1 -1
  59. package/dist/lang/en-us.umd.min.js +1 -1
  60. package/dist/lang/eo.umd.min.js +1 -1
  61. package/dist/lang/es.umd.min.js +1 -1
  62. package/dist/lang/et.umd.min.js +1 -1
  63. package/dist/lang/eu.umd.min.js +1 -1
  64. package/dist/lang/fa-ir.umd.min.js +1 -1
  65. package/dist/lang/fa.umd.min.js +1 -1
  66. package/dist/lang/fi.umd.min.js +1 -1
  67. package/dist/lang/fr.umd.min.js +1 -1
  68. package/dist/lang/gn.umd.min.js +1 -1
  69. package/dist/lang/he.umd.min.js +1 -1
  70. package/dist/lang/hr.umd.min.js +1 -1
  71. package/dist/lang/hu.umd.min.js +1 -1
  72. package/dist/lang/id.umd.min.js +1 -1
  73. package/dist/lang/is.umd.min.js +1 -1
  74. package/dist/lang/it.umd.min.js +1 -1
  75. package/dist/lang/ja.umd.min.js +1 -1
  76. package/dist/lang/km.umd.min.js +1 -1
  77. package/dist/lang/ko-kr.umd.min.js +1 -1
  78. package/dist/lang/kur-ckb.umd.min.js +1 -1
  79. package/dist/lang/kz.umd.min.js +1 -1
  80. package/dist/lang/lu.umd.min.js +1 -1
  81. package/dist/lang/lv.umd.min.js +1 -1
  82. package/dist/lang/ml.umd.min.js +1 -1
  83. package/dist/lang/mm.umd.min.js +1 -1
  84. package/dist/lang/ms.umd.min.js +1 -1
  85. package/dist/lang/my.umd.min.js +1 -1
  86. package/dist/lang/nb-no.umd.min.js +1 -1
  87. package/dist/lang/nl.umd.min.js +2 -2
  88. package/dist/lang/pl.umd.min.js +1 -1
  89. package/dist/lang/pt-br.umd.min.js +1 -1
  90. package/dist/lang/pt.umd.min.js +1 -1
  91. package/dist/lang/ro.umd.min.js +1 -1
  92. package/dist/lang/ru.umd.min.js +1 -1
  93. package/dist/lang/sk.umd.min.js +1 -1
  94. package/dist/lang/sl.umd.min.js +1 -1
  95. package/dist/lang/sm.umd.min.js +1 -1
  96. package/dist/lang/sr.umd.min.js +1 -1
  97. package/dist/lang/sv.umd.min.js +1 -1
  98. package/dist/lang/ta.umd.min.js +1 -1
  99. package/dist/lang/th.umd.min.js +1 -1
  100. package/dist/lang/tr.umd.min.js +1 -1
  101. package/dist/lang/ug.umd.min.js +1 -1
  102. package/dist/lang/uk.umd.min.js +1 -1
  103. package/dist/lang/uz-Cyrl.umd.min.js +1 -1
  104. package/dist/lang/uz-Latn.umd.min.js +1 -1
  105. package/dist/lang/vi.umd.min.js +1 -1
  106. package/dist/lang/zh-hans.umd.min.js +1 -1
  107. package/dist/lang/zh-hant.umd.min.js +1 -1
  108. package/dist/quasar.addon.css +1 -1
  109. package/dist/quasar.addon.rtl.css +1 -1
  110. package/dist/quasar.common.js +2 -2
  111. package/dist/quasar.css +43 -288
  112. package/dist/quasar.esm.js +2 -2
  113. package/dist/quasar.ie.polyfills.js +1 -1
  114. package/dist/quasar.ie.polyfills.umd.min.js +1 -1
  115. package/dist/quasar.min.css +1 -1
  116. package/dist/quasar.rtl.css +46 -408
  117. package/dist/quasar.rtl.min.css +1 -1
  118. package/dist/quasar.sass +3 -7
  119. package/dist/quasar.styl +3 -7
  120. package/dist/quasar.umd.js +1062 -867
  121. package/dist/quasar.umd.min.js +2 -2
  122. package/dist/quasar.umd.modern.js +1017 -854
  123. package/dist/quasar.umd.modern.min.js +2 -2
  124. package/dist/types/index.d.ts +8 -0
  125. package/dist/types/utils/run-sequential-promises.d.ts +1 -1
  126. package/dist/vetur/quasar-attributes.json +8 -0
  127. package/dist/vetur/quasar-tags.json +3 -1
  128. package/dist/web-types/web-types.json +66 -5
  129. package/lang/nl.js +2 -2
  130. package/package.json +1 -1
  131. package/src/body.js +1 -1
  132. package/src/components/avatar/QAvatar.js +1 -1
  133. package/src/components/badge/QBadge.js +1 -1
  134. package/src/components/banner/QBanner.js +1 -1
  135. package/src/components/bar/QBar.js +1 -1
  136. package/src/components/breadcrumbs/QBreadcrumbs.js +1 -1
  137. package/src/components/breadcrumbs/QBreadcrumbsEl.js +7 -4
  138. package/src/components/breadcrumbs/QBreadcrumbsEl.json +56 -0
  139. package/src/components/btn/QBtn.js +7 -29
  140. package/src/components/btn/QBtn.json +43 -5
  141. package/src/components/btn-dropdown/QBtnDropdown.js +2 -2
  142. package/src/components/btn-dropdown/QBtnDropdown.json +1 -1
  143. package/src/components/btn-group/QBtnGroup.js +1 -1
  144. package/src/components/btn-toggle/QBtnToggle.js +1 -1
  145. package/src/components/card/QCard.js +1 -1
  146. package/src/components/card/QCardActions.js +1 -1
  147. package/src/components/card/QCardSection.js +1 -1
  148. package/src/components/carousel/QCarousel.js +2 -2
  149. package/src/components/carousel/QCarouselControl.js +1 -1
  150. package/src/components/carousel/QCarouselSlide.js +1 -1
  151. package/src/components/checkbox/QCheckbox.js +1 -1
  152. package/src/components/chip/QChip.js +2 -2
  153. package/src/components/chip/QChip.json +1 -1
  154. package/src/components/circular-progress/QCircularProgress.js +1 -1
  155. package/src/components/color/QColor.js +1 -1
  156. package/src/components/date/QDate.js +3 -3
  157. package/src/components/dialog/QDialog.js +22 -7
  158. package/src/components/dialog/QDialog.json +7 -0
  159. package/src/components/dialog-bottom-sheet/BottomSheet.js +1 -1
  160. package/src/components/dialog-plugin/DialogPlugin.js +2 -2
  161. package/src/components/drawer/QDrawer.js +13 -7
  162. package/src/components/editor/QEditor.js +2 -2
  163. package/src/components/editor/editor-utils.js +2 -2
  164. package/src/components/expansion-item/QExpansionItem.js +29 -14
  165. package/src/components/expansion-item/QExpansionItem.json +1 -1
  166. package/src/components/fab/QFab.js +2 -2
  167. package/src/components/fab/QFabAction.js +1 -1
  168. package/src/components/field/QField.js +2 -2
  169. package/src/components/file/QFile.js +1 -1
  170. package/src/components/footer/QFooter.js +2 -2
  171. package/src/components/form/QForm.js +3 -3
  172. package/src/components/header/QHeader.js +2 -2
  173. package/src/components/icon/QIcon.js +1 -1
  174. package/src/components/img/QImg.js +1 -1
  175. package/src/components/infinite-scroll/QInfiniteScroll.js +1 -1
  176. package/src/components/input/QInput.js +1 -1
  177. package/src/components/intersection/QIntersection.js +1 -1
  178. package/src/components/item/QItem.js +39 -33
  179. package/src/components/item/QItem.json +56 -0
  180. package/src/components/item/QItemLabel.js +1 -1
  181. package/src/components/item/QItemSection.js +1 -1
  182. package/src/components/item/QList.js +1 -1
  183. package/src/components/knob/QKnob.js +2 -2
  184. package/src/components/layout/QLayout.js +2 -2
  185. package/src/components/linear-progress/QLinearProgress.js +1 -1
  186. package/src/components/markup-table/QMarkupTable.js +1 -1
  187. package/src/components/menu/ClickOutside.js +1 -1
  188. package/src/components/menu/QMenu.js +16 -7
  189. package/src/components/no-ssr/QNoSsr.js +1 -1
  190. package/src/components/option-group/QOptionGroup.js +1 -1
  191. package/src/components/page/QPage.js +1 -1
  192. package/src/components/page/QPageContainer.js +1 -1
  193. package/src/components/page-sticky/QPageSticky.js +1 -1
  194. package/src/components/pagination/QPagination.js +2 -2
  195. package/src/components/parallax/QParallax.js +1 -1
  196. package/src/components/popup-edit/QPopupEdit.js +6 -12
  197. package/src/components/popup-proxy/QPopupProxy.js +1 -1
  198. package/src/components/pull-to-refresh/QPullToRefresh.js +1 -1
  199. package/src/components/radio/QRadio.js +5 -4
  200. package/src/components/rating/QRating.js +46 -15
  201. package/src/components/rating/QRating.json +11 -0
  202. package/src/components/resize-observer/QResizeObserver.js +1 -1
  203. package/src/components/responsive/QResponsive.js +1 -1
  204. package/src/components/scroll-area/QScrollArea.js +2 -2
  205. package/src/components/select/QSelect.js +3 -3
  206. package/src/components/skeleton/QSkeleton.js +1 -1
  207. package/src/components/slide-item/QSlideItem.js +2 -2
  208. package/src/components/slide-transition/QSlideTransition.js +2 -2
  209. package/src/components/splitter/QSplitter.js +2 -2
  210. package/src/components/stepper/QStep.js +2 -2
  211. package/src/components/stepper/QStepper.js +2 -2
  212. package/src/components/stepper/QStepperNavigation.js +1 -1
  213. package/src/components/stepper/StepHeader.js +1 -1
  214. package/src/components/tab-panels/QTabPanel.js +1 -1
  215. package/src/components/table/QTable.js +1 -1
  216. package/src/components/table/QTd.js +1 -1
  217. package/src/components/table/QTh.js +1 -1
  218. package/src/components/table/QTr.js +1 -1
  219. package/src/components/table/table-body.js +21 -32
  220. package/src/components/table/table-bottom.js +1 -1
  221. package/src/components/table/table-header.js +9 -9
  222. package/src/components/table/table-sort.js +1 -1
  223. package/src/components/tabs/QRouteTab.js +10 -120
  224. package/src/components/tabs/QRouteTab.json +7 -2
  225. package/src/components/tabs/QTab.js +112 -75
  226. package/src/components/tabs/QTabs.js +411 -329
  227. package/src/components/tabs/QTabs.sass +2 -7
  228. package/src/components/tabs/QTabs.styl +2 -7
  229. package/src/components/time/QTime.js +2 -2
  230. package/src/components/timeline/QTimeline.js +1 -1
  231. package/src/components/timeline/QTimelineEntry.js +1 -1
  232. package/src/components/toolbar/QToolbar.js +1 -1
  233. package/src/components/toolbar/QToolbarTitle.js +1 -1
  234. package/src/components/tooltip/QTooltip.js +19 -15
  235. package/src/components/tree/QTree.js +17 -15
  236. package/src/components/uploader/QUploaderBase.js +1 -1
  237. package/src/components/virtual-scroll/QVirtualScroll.js +1 -1
  238. package/src/directives/ClosePopup.js +1 -1
  239. package/src/directives/GoBack.js +1 -1
  240. package/src/directives/Ripple.js +1 -1
  241. package/src/directives/TouchHold.js +2 -2
  242. package/src/directives/TouchPan.js +4 -4
  243. package/src/directives/TouchRepeat.js +3 -3
  244. package/src/directives/TouchSwipe.js +3 -3
  245. package/src/mixins/anchor.js +2 -2
  246. package/src/mixins/attrs.js +1 -1
  247. package/src/mixins/btn.js +4 -3
  248. package/src/mixins/checkbox.js +4 -3
  249. package/src/mixins/datetime.js +1 -1
  250. package/src/mixins/file.js +1 -1
  251. package/src/mixins/listeners.js +1 -1
  252. package/src/mixins/mask.js +1 -1
  253. package/src/mixins/model-toggle.js +5 -7
  254. package/src/mixins/panel.js +2 -2
  255. package/src/mixins/portal.js +1 -1
  256. package/src/mixins/router-link.js +164 -38
  257. package/src/mixins/timeout.js +57 -26
  258. package/src/plugins/BottomSheet.js +1 -1
  259. package/src/plugins/BottomSheet.json +1 -1
  260. package/src/plugins/Dialog.js +1 -1
  261. package/src/plugins/Dialog.json +1 -1
  262. package/src/plugins/Loading.js +1 -1
  263. package/src/plugins/LocalStorage.js +1 -1
  264. package/src/plugins/LocalStorage.json +1 -1
  265. package/src/plugins/SessionStorage.js +1 -1
  266. package/src/plugins/SessionStorage.json +1 -1
  267. package/src/utils/date.js +1 -1
  268. package/src/utils/extend.js +15 -19
  269. package/src/utils/{cache.js → private/cache.js} +1 -1
  270. package/src/utils/{date-persian.js → private/date-persian.js} +0 -0
  271. package/src/utils/{escape-key.js → private/escape-key.js} +0 -0
  272. package/src/utils/{focus-manager.js → private/focus-manager.js} +0 -0
  273. package/src/utils/{global-dialog.js → private/global-dialog.js} +1 -1
  274. package/src/utils/{global-dialog.json → private/global-dialog.json} +0 -0
  275. package/src/utils/private/inject-obj-prop.js +15 -0
  276. package/src/utils/{key-composition.js → private/key-composition.js} +0 -0
  277. package/src/utils/{position-engine.js → private/position-engine.js} +2 -2
  278. package/src/utils/{selection.js → private/selection.js} +1 -1
  279. package/src/utils/{slot.js → private/slot.js} +0 -0
  280. package/src/utils/{sort.js → private/sort.js} +0 -0
  281. package/src/utils/{touch.js → private/touch.js} +0 -0
  282. package/src/utils/{vm.js → private/vm.js} +0 -0
  283. package/src/utils/{web-storage.js → private/web-storage.js} +2 -2
  284. package/src/utils/{web-storage.json → private/web-storage.json} +0 -0
  285. package/src/utils/run-sequential-promises.js +1 -1
  286. package/src/utils.js +4 -4
  287. package/src/utils/router.js +0 -50
@@ -7,9 +7,10 @@ import TimeoutMixin from '../../mixins/timeout.js'
7
7
  import ListenersMixin from '../../mixins/listeners.js'
8
8
 
9
9
  import { stop, noop } from '../../utils/event.js'
10
- import { slot } from '../../utils/slot.js'
11
- import cache from '../../utils/cache.js'
10
+ import { slot } from '../../utils/private/slot.js'
11
+ import cache from '../../utils/private/cache.js'
12
12
  import { rtlHasScrollBug } from '../../utils/scroll.js'
13
+ import { injectProp } from '../../utils/private/inject-obj-prop.js'
13
14
 
14
15
  function getIndicatorClass (color, top, vertical) {
15
16
  const pos = vertical === true
@@ -19,44 +20,26 @@ function getIndicatorClass (color, top, vertical) {
19
20
  return `absolute-${top === true ? pos[0] : pos[1]}${color ? ` text-${color}` : ''}`
20
21
  }
21
22
 
22
- function bufferPrioritySort (t1, t2) {
23
- if (t1.priorityMatched === t2.priorityMatched) {
24
- return t2.priorityHref - t1.priorityHref
23
+ const alignValues = [ 'left', 'center', 'right', 'justify' ]
24
+ const getDefaultBestScore = () => ({ matchedLen: 0, queryDiff: 9999, hrefLen: 0, exact: false, redirected: true })
25
+
26
+ function hasQueryIncluded (targetQuery, matchingQuery) {
27
+ for (const key in targetQuery) {
28
+ if (targetQuery[ key ] !== matchingQuery[ key ]) {
29
+ return false
30
+ }
25
31
  }
26
- return t2.priorityMatched - t1.priorityMatched
27
- }
28
32
 
29
- function bufferCleanSelected (t) {
30
- t.selected = false
31
- return t
33
+ return true
32
34
  }
33
35
 
34
- const
35
- bufferFilters = [
36
- t => t.selected === true && t.exact === true && t.redirected !== true,
37
- t => t.selected === true && t.exact === true,
38
- t => t.selected === true && t.redirected !== true,
39
- t => t.selected === true,
40
- t => t.exact === true && t.redirected !== true,
41
- t => t.redirected !== true,
42
- t => t.exact === true,
43
- t => true
44
- ],
45
- bufferFiltersLen = bufferFilters.length
46
-
47
36
  export default Vue.extend({
48
37
  name: 'QTabs',
49
38
 
50
39
  mixins: [ TimeoutMixin, ListenersMixin ],
51
40
 
52
41
  provide () {
53
- return {
54
- tabs: this.tabs,
55
- __recalculateScroll: this.__recalculateScroll,
56
- __activateTab: this.__activateTab,
57
- __activateRoute: this.__activateRoute,
58
- __onKbdNavigate: this.__onKbdNavigate
59
- }
42
+ return { $tabs: this }
60
43
  },
61
44
 
62
45
  props: {
@@ -65,7 +48,7 @@ export default Vue.extend({
65
48
  align: {
66
49
  type: String,
67
50
  default: 'center',
68
- validator: v => ['left', 'center', 'right', 'justify'].includes(v)
51
+ validator: v => alignValues.includes(v)
69
52
  },
70
53
  breakpoint: {
71
54
  type: [String, Number],
@@ -99,67 +82,27 @@ export default Vue.extend({
99
82
 
100
83
  data () {
101
84
  return {
102
- tabs: {
103
- current: this.value,
104
- hasFocus: false,
105
- activeClass: this.activeClass,
106
- activeColor: this.activeColor,
107
- activeBgColor: this.activeBgColor,
108
- indicatorClass: getIndicatorClass(
109
- this.indicatorColor,
110
- this.switchIndicator,
111
- this.vertical
112
- ),
113
- narrowIndicator: this.narrowIndicator,
114
- inlineLabel: this.inlineLabel,
115
- noCaps: this.noCaps
116
- },
117
85
  scrollable: false,
118
- startArrow: true,
119
- endArrow: false,
120
- justify: false
86
+ leftArrow: true,
87
+ rightArrow: false,
88
+ justify: false,
89
+
90
+ tabNameList: [],
91
+
92
+ // used by children
93
+ currentModel: this.value,
94
+ hasFocus: false,
95
+ avoidRouteWatcher: false // false | string (uid)
121
96
  }
122
97
  },
123
98
 
124
99
  watch: {
125
- value (name) {
126
- this.__activateTab(name, true, true)
127
- },
128
-
129
- activeClass (v) {
130
- this.tabs.activeClass = v
131
- },
132
-
133
- activeColor (v) {
134
- this.tabs.activeColor = v
135
- },
136
-
137
- activeBgColor (v) {
138
- this.tabs.activeBgColor = v
139
- },
140
-
141
- vertical (v) {
142
- this.tabs.indicatorClass = getIndicatorClass(this.indicatorColor, this.switchIndicator, v)
143
- },
144
-
145
- indicatorColor (v) {
146
- this.tabs.indicatorClass = getIndicatorClass(v, this.switchIndicator, this.vertical)
147
- },
148
-
149
- switchIndicator (v) {
150
- this.tabs.indicatorClass = getIndicatorClass(this.indicatorColor, v, this.vertical)
151
- },
152
-
153
- narrowIndicator (v) {
154
- this.tabs.narrowIndicator = v
155
- },
156
-
157
- inlineLabel (v) {
158
- this.tabs.inlineLabel = v
100
+ isRTL () {
101
+ this.__localUpdateArrows()
159
102
  },
160
103
 
161
- noCaps (v) {
162
- this.tabs.noCaps = v
104
+ value (name) {
105
+ this.__updateModel({ name, setCurrent: true, skipEmit: true })
163
106
  },
164
107
 
165
108
  outsideArrows () {
@@ -167,32 +110,39 @@ export default Vue.extend({
167
110
  },
168
111
 
169
112
  arrowsEnabled (v) {
170
- this.__updateArrows = v === true
113
+ this.__localUpdateArrows = v === true
171
114
  ? this.__updateArrowsFn
172
115
  : noop
173
116
 
174
117
  this.__recalculateScroll()
175
- },
176
-
177
- isRTL () {
178
- this.__updateArrows()
179
118
  }
180
119
  },
181
120
 
182
121
  computed: {
183
- arrowsEnabled () {
184
- return this.$q.platform.is.desktop === true || this.mobileArrows === true
122
+ // used by children
123
+ tabProps () {
124
+ return {
125
+ activeClass: this.activeClass,
126
+ activeColor: this.activeColor,
127
+ activeBgColor: this.activeBgColor,
128
+ indicatorClass: getIndicatorClass(
129
+ this.indicatorColor,
130
+ this.switchIndicator,
131
+ this.vertical
132
+ ),
133
+ narrowIndicator: this.narrowIndicator,
134
+ inlineLabel: this.inlineLabel,
135
+ noCaps: this.noCaps
136
+ }
185
137
  },
186
138
 
187
- arrowIcons () {
188
- const sides = this.isRTL === true
189
- ? [ 'end', 'start' ]
190
- : [ 'start', 'end' ]
139
+ // used by children
140
+ hasActiveTab () {
141
+ return this.tabNameList.some(entry => entry.name === this.currentModel)
142
+ },
191
143
 
192
- return {
193
- [sides[0]]: this.leftIcon || (this.vertical === true ? this.$q.iconSet.tabs.up : this.$q.iconSet.tabs.left),
194
- [sides[1]]: this.rightIcon || (this.vertical === true ? this.$q.iconSet.tabs.down : this.$q.iconSet.tabs.right)
195
- }
144
+ arrowsEnabled () {
145
+ return this.$q.platform.is.desktop === true || this.mobileArrows === true
196
146
  },
197
147
 
198
148
  alignClass () {
@@ -204,7 +154,8 @@ export default Vue.extend({
204
154
  },
205
155
 
206
156
  classes () {
207
- return `q-tabs--${this.scrollable === true ? '' : 'not-'}scrollable` +
157
+ return 'q-tabs row no-wrap items-center' +
158
+ ` q-tabs--${this.scrollable === true ? '' : 'not-'}scrollable` +
208
159
  ` q-tabs--${this.vertical === true ? 'vertical' : 'horizontal'}` +
209
160
  ` q-tabs__arrows--${this.arrowsEnabled === true && this.outsideArrows === true ? 'outside' : 'inside'}` +
210
161
  (this.dense === true ? ' q-tabs--dense' : '') +
@@ -213,7 +164,8 @@ export default Vue.extend({
213
164
  },
214
165
 
215
166
  innerClass () {
216
- return this.alignClass +
167
+ return 'q-tabs__content row no-wrap items-center self-stretch hide-scrollbar relative-position ' +
168
+ this.alignClass +
217
169
  (this.contentClass !== void 0 ? ` ${this.contentClass}` : '') +
218
170
  (this.$q.platform.is.mobile === true ? ' scroll' : '')
219
171
  },
@@ -228,41 +180,20 @@ export default Vue.extend({
228
180
  return this.vertical !== true && this.$q.lang.rtl === true
229
181
  },
230
182
 
231
- __getScrollPosition () {
232
- return this.vertical === true
233
- ? el => el.scrollTop
234
- : (
235
- this.$q.lang.rtl !== true
236
- ? el => el.scrollLeft
237
- : (
238
- this.rtlHasScrollBug === true
239
- ? el => el.scrollWidth - el.clientWidth - el.scrollLeft
240
- : el => 1 - el.scrollLeft
241
- )
242
- )
243
- },
244
-
245
- __setScrollPosition () {
246
- return this.vertical === true
247
- ? (el, value) => { el.scrollTop = value }
248
- : (
249
- this.$q.lang.rtl !== true
250
- ? (el, value) => { el.scrollLeft = value }
251
- : (
252
- this.rtlHasScrollBug === true
253
- ? (el, value) => { el.scrollLeft = el.scrollWidth - el.clientWidth - value }
254
- : (el, value) => { el.scrollLeft = 1 - value }
255
- )
256
- )
183
+ rtlPosCorrection () {
184
+ return rtlHasScrollBug() === false && this.isRTL === true
257
185
  },
258
186
 
259
- __getScrollOffset () {
260
- return this.vertical === true
261
- ? el => el.offsetTop
187
+ // let's speed up execution of time-sensitive scrollTowards()
188
+ // with a computed variable by directly applying the minimal
189
+ // number of instructions on get/set functions
190
+ posFn () {
191
+ return this.rtlPosCorrection === true
192
+ ? { get: content => Math.abs(content.scrollLeft), set: (content, pos) => { content.scrollLeft = -pos } }
262
193
  : (
263
- this.$q.lang.rtl !== true || this.rtlHasScrollBug === true
264
- ? el => el.offsetLeft
265
- : el => el.offsetParent.offsetWidth - el.offsetLeft - el.clientWidth
194
+ this.vertical === true
195
+ ? { get: content => content.scrollTop, set: (content, pos) => { content.scrollTop = pos } }
196
+ : { get: content => content.scrollLeft, set: (content, pos) => { content.scrollLeft = pos } }
266
197
  )
267
198
  },
268
199
 
@@ -277,78 +208,42 @@ export default Vue.extend({
277
208
  },
278
209
 
279
210
  methods: {
280
- __onFocusin (e) {
281
- this.tabs.hasFocus = true
282
- this.qListeners.focusin !== void 0 && this.$emit('focusin', e)
283
- },
284
-
285
- __onFocusout (e) {
286
- this.tabs.hasFocus = false
287
- this.qListeners.focusout !== void 0 && this.$emit('focusout', e)
288
- },
289
-
290
- __activateTab (name, setCurrent, skipEmit) {
291
- if (this.tabs.current !== name) {
292
- skipEmit !== true && this.$emit('input', name)
293
- if (setCurrent === true || this.qListeners.input === void 0) {
294
- this.__animate(this.tabs.current, name)
295
- this.tabs.current = name
211
+ // used by children too
212
+ __updateModel ({ name, setCurrent, skipEmit }) {
213
+ if (this.currentModel !== name) {
214
+ if (skipEmit !== true && this.qListeners.input !== void 0) {
215
+ this.$emit('input', name)
296
216
  }
297
- }
298
- },
299
-
300
- __activateRoute (params) {
301
- if (this.bufferRoute !== this.$route && this.buffer.length > 0) {
302
- clearTimeout(this.bufferTimer)
303
- this.bufferTimer = void 0
304
- this.buffer.length = 0
305
- }
306
- this.bufferRoute = this.$route
307
217
 
308
- if (params !== void 0) {
309
- if (params.remove === true) {
310
- this.buffer = this.buffer.filter(t => t.name !== params.name)
311
- }
312
- else {
313
- this.buffer.push(params)
218
+ if (setCurrent === true || this.qListeners.input === void 0) {
219
+ this.__animate(this.currentModel, name)
220
+ this.currentModel = name
314
221
  }
315
222
  }
316
-
317
- if (this.bufferTimer === void 0) {
318
- this.bufferTimer = setTimeout(() => {
319
- let tabs = []
320
-
321
- for (let i = 0; i < bufferFiltersLen && tabs.length === 0; i++) {
322
- tabs = this.buffer.filter(bufferFilters[i])
323
- }
324
-
325
- tabs.sort(bufferPrioritySort)
326
- this.__activateTab(tabs.length === 0 ? null : tabs[0].name, true)
327
- this.buffer = this.buffer.map(bufferCleanSelected)
328
- this.bufferTimer = void 0
329
- }, 1)
330
- }
331
223
  },
332
224
 
333
225
  __recalculateScroll () {
334
- this.__nextTick(() => {
335
- this._isDestroyed !== true && this.__updateContainer({
226
+ this.__registerScrollTick(() => {
227
+ this.__updateContainer({
336
228
  width: this.$el.offsetWidth,
337
229
  height: this.$el.offsetHeight
338
230
  })
339
231
  })
340
-
341
- this.__prepareTick()
342
232
  },
343
233
 
344
234
  __updateContainer (domSize) {
235
+ // it can be called faster than component being initialized
236
+ // so we need to protect against that case
237
+ // (one example of such case is the docs release notes page)
238
+ if (this.domProps === void 0 || !this.$refs.content) { return }
239
+
345
240
  const
346
- size = domSize[this.domProps.container],
241
+ size = domSize[ this.domProps.container ],
347
242
  scrollSize = Math.min(
348
243
  this.$refs.content[this.domProps.scroll],
349
244
  Array.prototype.reduce.call(
350
245
  this.$refs.content.children,
351
- (acc, el) => acc + (el[this.domProps.content] || 0),
246
+ (acc, el) => acc + (el[ this.domProps.content ] || 0),
352
247
  0
353
248
  )
354
249
  ),
@@ -359,28 +254,28 @@ export default Vue.extend({
359
254
  }
360
255
 
361
256
  // Arrows need to be updated even if the scroll status was already true
362
- scroll === true && this.$nextTick(() => this.__updateArrows())
257
+ scroll === true && this.__registerUpdateArrowsTick(this.__localUpdateArrows)
363
258
 
364
- const justify = size < parseInt(this.breakpoint, 10)
259
+ const localJustify = size < parseInt(this.breakpoint, 10)
365
260
 
366
- if (this.justify !== justify) {
367
- this.justify = justify
261
+ if (this.justify !== localJustify) {
262
+ this.justify = localJustify
368
263
  }
369
264
  },
370
265
 
371
266
  __animate (oldName, newName) {
372
267
  const
373
268
  oldTab = oldName !== void 0 && oldName !== null && oldName !== ''
374
- ? this.$children.find(tab => tab.name === oldName)
269
+ ? this.tabVmList.find(tab => tab.name === oldName)
375
270
  : null,
376
271
  newTab = newName !== void 0 && newName !== null && newName !== ''
377
- ? this.$children.find(tab => tab.name === newName)
272
+ ? this.tabVmList.find(tab => tab.name === newName)
378
273
  : null
379
274
 
380
275
  if (oldTab && newTab) {
381
276
  const
382
- oldEl = oldTab.$el.getElementsByClassName('q-tab__indicator')[0],
383
- newEl = newTab.$el.getElementsByClassName('q-tab__indicator')[0]
277
+ oldEl = oldTab.$refs.tabIndicator,
278
+ newEl = newTab.$refs.tabIndicator
384
279
 
385
280
  clearTimeout(this.animateTimer)
386
281
 
@@ -398,7 +293,7 @@ export default Vue.extend({
398
293
  : `translate3d(${oldPos.left - newPos.left}px,0,0) scale3d(${newPos.width ? oldPos.width / newPos.width : 1},1,1)`
399
294
 
400
295
  // allow scope updates to kick in (QRouteTab needs more time)
401
- this.$nextTick(() => {
296
+ this.__registerAnimateTick(() => {
402
297
  this.animateTimer = setTimeout(() => {
403
298
  newEl.style.transition = 'transform .25s cubic-bezier(.4, 0, .2, 1)'
404
299
  newEl.style.transform = 'none'
@@ -407,72 +302,129 @@ export default Vue.extend({
407
302
  }
408
303
 
409
304
  if (newTab && this.scrollable === true) {
410
- this.__scrollToTab(newTab.$el, void 0, true)
305
+ this.__scrollToTabEl(newTab.$el)
411
306
  }
412
- else {
413
- this.__updateArrows()
307
+ },
308
+
309
+ __scrollToTabEl (el) {
310
+ const
311
+ contentRef = this.$refs.content,
312
+ { left, width, top, height } = contentRef.getBoundingClientRect(),
313
+ newPos = el.getBoundingClientRect()
314
+
315
+ let offset = this.vertical === true ? newPos.top - top : newPos.left - left
316
+
317
+ if (offset < 0) {
318
+ contentRef[ this.vertical === true ? 'scrollTop' : 'scrollLeft' ] += Math.floor(offset)
319
+ this.__localUpdateArrows()
320
+ return
321
+ }
322
+
323
+ offset += this.vertical === true ? newPos.height - height : newPos.width - width
324
+ if (offset > 0) {
325
+ contentRef[ this.vertical === true ? 'scrollTop' : 'scrollLeft' ] += Math.ceil(offset)
326
+ this.__localUpdateArrows()
414
327
  }
415
328
  },
416
329
 
417
330
  __updateArrowsFn () {
418
- const { content } = this.$refs
419
-
420
- if (content !== void 0) {
331
+ const content = this.$refs.content
332
+ if (content !== null) {
421
333
  const
422
334
  rect = content.getBoundingClientRect(),
423
- pos = this.__getScrollPosition(content)
335
+ pos = this.vertical === true ? content.scrollTop : Math.abs(content.scrollLeft)
424
336
 
425
- this.startArrow = pos > (this.isRTL === true ? 1 : 0)
426
- this.endArrow = this.vertical === true
427
- ? Math.ceil(pos + rect.height) < content.scrollHeight
428
- : Math.ceil(pos + rect.width) < content.scrollWidth
337
+ if (this.isRTL === true) {
338
+ this.leftArrow = Math.ceil(pos + rect.width) < content.scrollWidth - 1
339
+ this.rightArrow = pos > 0
340
+ }
341
+ else {
342
+ this.leftArrow = pos > 0
343
+ this.rightArrow = this.vertical === true
344
+ ? Math.ceil(pos + rect.height) < content.scrollHeight
345
+ : Math.ceil(pos + rect.width) < content.scrollWidth
346
+ }
429
347
  }
430
348
  },
431
349
 
432
- __animScrollTo (value, onEnd) {
350
+ __animScrollTo (value) {
433
351
  this.__stopAnimScroll()
434
-
435
- this.__onAnimScrollEnd = onEnd
436
- this.__scrollTowards(value)
437
-
438
352
  this.scrollTimer = setInterval(() => {
439
- if (this.__scrollTowards(value)) {
353
+ if (this.__scrollTowards(value) === true) {
440
354
  this.__stopAnimScroll()
441
355
  }
442
356
  }, 5)
443
357
  },
444
358
 
445
359
  __scrollToStart () {
446
- this.__animScrollTo(0)
360
+ this.__animScrollTo(this.rtlPosCorrection === true ? Number.MAX_SAFE_INTEGER : 0)
447
361
  },
448
362
 
449
363
  __scrollToEnd () {
450
- this.__animScrollTo(Number.MAX_SAFE_INTEGER)
364
+ this.__animScrollTo(this.rtlPosCorrection === true ? 0 : Number.MAX_SAFE_INTEGER)
451
365
  },
452
366
 
453
367
  __stopAnimScroll () {
454
368
  clearInterval(this.scrollTimer)
369
+ },
455
370
 
456
- if (this.__onAnimScrollEnd !== void 0) {
457
- this.__onAnimScrollEnd()
458
- this.__onAnimScrollEnd = void 0
371
+ // used by children
372
+ __onKbdNavigate (keyCode, fromEl) {
373
+ const tabs = Array.prototype.filter.call(
374
+ this.$refs.content.children,
375
+ el => el === fromEl || (el.matches && el.matches('.q-tab.q-focusable') === true)
376
+ )
377
+
378
+ const len = tabs.length
379
+ if (len === 0) { return }
380
+
381
+ if (keyCode === 36) { // Home
382
+ this.__scrollToTabEl(tabs[ 0 ])
383
+ tabs[ 0 ].focus()
384
+ return true
385
+ }
386
+ if (keyCode === 35) { // End
387
+ this.__scrollToTabEl(tabs[ len - 1 ])
388
+ tabs[ len - 1 ].focus()
389
+ return true
390
+ }
391
+
392
+ const dirPrev = keyCode === (this.vertical === true ? 38 /* ArrowUp */ : 37 /* ArrowLeft */)
393
+ const dirNext = keyCode === (this.vertical === true ? 40 /* ArrowDown */ : 39 /* ArrowRight */)
394
+
395
+ const dir = dirPrev === true ? -1 : (dirNext === true ? 1 : void 0)
396
+
397
+ if (dir !== void 0) {
398
+ const rtlDir = this.isRTL === true ? -1 : 1
399
+ const index = tabs.indexOf(fromEl) + dir * rtlDir
400
+
401
+ if (index >= 0 && index < len) {
402
+ this.__scrollToTabEl(tabs[ index ])
403
+ tabs[ index ].focus({ preventScroll: true })
404
+ }
405
+
406
+ return true
459
407
  }
460
408
  },
461
409
 
462
410
  __scrollTowards (value) {
463
- const content = this.$refs.content
464
- const max = this.vertical === true ? content.scrollHeight - content.offsetHeight : content.scrollWidth - content.offsetWidth
465
-
466
- let pos = this.__getScrollPosition(content)
467
- let done = false
411
+ const
412
+ content = this.$refs.content,
413
+ { get, set } = this.posFn
468
414
 
469
- value = Math.max(0, Math.min(max, value))
415
+ let
416
+ done = false,
417
+ pos = get(content)
470
418
 
471
419
  const direction = value < pos ? -1 : 1
472
420
 
473
421
  pos += direction * 5
474
422
 
475
- if (
423
+ if (pos < 0) {
424
+ done = true
425
+ pos = 0
426
+ }
427
+ else if (
476
428
  (direction === -1 && pos <= value) ||
477
429
  (direction === 1 && pos >= value)
478
430
  ) {
@@ -480,140 +432,272 @@ export default Vue.extend({
480
432
  pos = value
481
433
  }
482
434
 
483
- this.__setScrollPosition(content, pos)
484
- this.__updateArrows()
435
+ set(content, pos)
436
+ this.__localUpdateArrows()
485
437
 
486
438
  return done
487
439
  },
488
440
 
489
- __scrollToTab (tab, alignEnd, skipFocus) {
490
- if (this.$refs.content === void 0) {
491
- return
492
- }
441
+ // do not use directly; use __verifyRouteModel() instead
442
+ __updateActiveRoute () {
443
+ let name = null, bestScore = getDefaultBestScore()
493
444
 
494
- const content = this.$refs.content
495
- const startContent = this.__getScrollPosition(content)
496
- const sizeContent = this.vertical === true ? content.offsetHeight : content.offsetWidth
497
- const sizeScroll = this.vertical === true ? content.scrollHeight : content.scrollWidth
445
+ const vmList = this.tabVmList.filter(tab => tab.hasRouterLink === true)
446
+ const vmLen = vmList.length
498
447
 
499
- const startTab = this.__getScrollOffset(tab)
500
- const endTab = startTab + (this.vertical === true ? tab.offsetHeight : tab.offsetWidth)
448
+ const { query: currentQuery } = this.$route
449
+ const currentQueryLen = Object.keys(currentQuery).length
501
450
 
502
- const startsBefore = startTab < startContent
503
- const endsAfter = endTab > startContent + sizeContent
451
+ for (let tabIndex = 0; tabIndex < vmLen; tabIndex++) {
452
+ const tab = vmList[tabIndex]
453
+ const exact = tab.exact === true
504
454
 
505
- if (startsBefore !== true && endsAfter !== true) {
506
- alignEnd = void 0
507
- }
508
- else if (alignEnd === void 0) {
509
- if (endTab >= sizeScroll - 1) {
510
- alignEnd = true
455
+ // if a) it doesn't respect the active/exact active status
456
+ // or b) we already found an exact match and this one isn't set as exact
457
+ // then we don't deal with it
458
+ if (
459
+ tab[ exact === true ? 'linkIsExactActive' : 'linkIsActive' ] !== true ||
460
+ (bestScore.exact === true && exact !== true)
461
+ ) {
462
+ continue
511
463
  }
512
- else if (startsBefore === true || (endsAfter === true && startTab < endTab - sizeContent)) {
513
- alignEnd = false
464
+
465
+ const { route, href } = tab.resolvedLink
466
+ const { matched, query, hash } = route
467
+ const redirected = route.redirectedFrom !== void 0
468
+
469
+ if (exact === true) {
470
+ // hey, we found the perfect match; no more searching!
471
+ if (redirected === false) {
472
+ name = tab.name
473
+ break
474
+ }
475
+
476
+ if (bestScore.exact === false) {
477
+ // we reset values so we can discard previous non-exact matches
478
+ // and so we can register this exact one below
479
+ bestScore = getDefaultBestScore()
480
+ }
514
481
  }
515
- else if (startsBefore !== endsAfter) {
516
- alignEnd = endsAfter
482
+
483
+ // if best is non-redirected and this one is redirected
484
+ // then this one is inferior so we don't care about it
485
+ if (bestScore.redirected === false && redirected === true) {
486
+ continue
517
487
  }
518
- }
519
488
 
520
- if (alignEnd !== void 0) {
521
- this.__animScrollTo(
522
- alignEnd === true ? (endTab >= sizeScroll - 1 ? sizeScroll : endTab - sizeContent) : (startTab <= 1 ? 0 : startTab),
523
- skipFocus !== true
524
- ? () => {
525
- setTimeout(() => {
526
- tab && tab.focus()
527
- })
528
- }
529
- : void 0
530
- )
489
+ const queryLen = Object.keys(query).length
490
+
491
+ if (
492
+ // if it's exact it already perfectly includes current query
493
+ // so no point in computing it
494
+ exact === false &&
495
+ queryLen !== 0 &&
496
+ hasQueryIncluded(query, currentQuery) === false
497
+ ) {
498
+ // it has query and it doesn't includes the current one
499
+ continue
500
+ }
501
+
502
+ const newScore = {
503
+ exact,
504
+ redirected,
505
+ matchedLen: matched.length,
506
+ queryDiff: exact === true
507
+ ? 0 // avoid computing as it's 0 anyway
508
+ : currentQueryLen - queryLen,
509
+ hrefLen: href.length - hash.length
510
+ }
511
+
512
+ if (newScore.matchedLen > bestScore.matchedLen) {
513
+ // it matches more routes so it's more specific so we set it as current champion
514
+ name = tab.name
515
+ bestScore = newScore
516
+ continue
517
+ }
518
+ else if (newScore.matchedLen !== bestScore.matchedLen) {
519
+ // it matches less routes than the current champion so we discard it
520
+ continue
521
+ }
522
+
523
+ if (newScore.queryDiff < bestScore.queryDiff) {
524
+ // query is closer to the current one so we set it as current champion
525
+ name = tab.name
526
+ bestScore = newScore
527
+ continue
528
+ }
529
+ else if (newScore.queryDiff !== bestScore.queryDiff) {
530
+ continue
531
+ }
532
+
533
+ if (newScore.hrefLen > bestScore.hrefLen) {
534
+ // href is lengthier so it's more specific so we set it as current champion
535
+ name = tab.name
536
+ bestScore = newScore
537
+ }
531
538
  }
532
- else if (skipFocus !== true) {
533
- tab.focus()
539
+
540
+ if (
541
+ name === null &&
542
+ this.tabVmList.some(tab => tab.hasRouterLink === void 0 && tab.name === this.currentModel) === true
543
+ ) {
544
+ // we shouldn't interfere if non-route tab is active
545
+ return
534
546
  }
547
+
548
+ this.__updateModel({ name, setCurrent: true })
535
549
  },
536
550
 
537
- __onKbdNavigate (keyCode, fromEl) {
538
- const matchTab = el => el === fromEl || (el.matches && el.matches('.q-tab.q-focusable') === true)
539
- const tabs = Array.prototype.filter.call(this.$refs.content.children, matchTab)
540
- const tabsLength = tabs.length
551
+ __onFocusin (e) {
552
+ this.__removeFocusTimeout()
541
553
 
542
- if (tabsLength === 0) {
543
- return
544
- }
554
+ if (
555
+ this.hasFocus !== true &&
556
+ this.$el &&
557
+ e.target &&
558
+ typeof e.target.closest === 'function'
559
+ ) {
560
+ const tab = e.target.closest('.q-tab')
545
561
 
546
- if (keyCode === 36) { // Home
547
- if (tabs[0].contains(document.activeElement) === true) {
548
- return false
562
+ // if the target is contained by a QTab/QRouteTab
563
+ // (it might be other elements focused, like additional QBtn)
564
+ if (tab && this.$el.contains(tab) === true) {
565
+ this.hasFocus = true
566
+
567
+ if (this.scrollable === true) {
568
+ this.__scrollToTabEl(tab)
569
+ }
549
570
  }
571
+ }
550
572
 
551
- this.__scrollToTab(tabs[0], false)
552
- this.__recalculateScroll()
573
+ this.qListeners.focusin !== void 0 && this.$emit('focusin', e)
574
+ },
553
575
 
554
- return true
576
+ __onFocusout (e) {
577
+ this.__registerFocusTimeout(() => { this.hasFocus = false }, 30)
578
+ this.qListeners.focusout !== void 0 && this.$emit('focusout', e)
579
+ },
580
+
581
+ // used by children
582
+ __verifyRouteModel () {
583
+ if (this.avoidRouteWatcher === false) {
584
+ this.__registerScrollToTabTimeout(this.__updateActiveRoute)
555
585
  }
556
- if (keyCode === 35) { // End
557
- if (tabs[tabsLength - 1].contains(document.activeElement) === true) {
558
- return false
586
+ else {
587
+ this.__removeScrollToTabTimeout()
588
+ }
589
+ },
590
+
591
+ __watchRoute () {
592
+ if (this.unwatchRoute === void 0) {
593
+ const unwatch = this.$watch(() => this.$route.fullPath, this.__verifyRouteModel)
594
+ this.unwatchRoute = () => {
595
+ unwatch()
596
+ this.unwatchRoute = void 0
559
597
  }
598
+ }
599
+ },
560
600
 
561
- this.__scrollToTab(tabs[tabsLength - 1], true)
562
- this.__recalculateScroll()
601
+ // used by children
602
+ __registerTab (tabVm) {
603
+ // we avoid setting tabVmList in data() as this would
604
+ // make the whole vm reactive
605
+ this.tabVmList.push(tabVm)
606
+ // ...so we extract only the needed stuff out of it
607
+ // into data() defined tabNameList
608
+ this.tabNameList.push(
609
+ injectProp({}, 'name', () => tabVm.name)
610
+ )
563
611
 
564
- return true
612
+ this.__recalculateScroll()
613
+
614
+ // if it's a QTab
615
+ if (tabVm.hasRouterLink === void 0) {
616
+ // we should position to the currently active tab (if any)
617
+ this.__registerScrollToTabTimeout(() => {
618
+ if (this.scrollable === true) {
619
+ const value = this.currentModel
620
+ const newTab = value !== void 0 && value !== null && value !== ''
621
+ ? this.tabVmList.find(tab => tab.name === value)
622
+ : null
623
+
624
+ newTab && this.__scrollToTabEl(newTab.$el)
625
+ }
626
+ })
565
627
  }
628
+ // else if it's a QRouteTab with a valid link
629
+ else {
630
+ this.__watchRoute()
566
631
 
567
- const dirPrev = (this.vertical === true && keyCode === 38 /* ArrowUp */) ||
568
- (this.vertical !== true && keyCode === 37 /* ArrowLeft */)
569
- const dirNext = (this.vertical === true && keyCode === 40 /* ArrowDown */) ||
570
- (this.vertical !== true && keyCode === 39 /* ArrowRight */)
571
- const dir = dirPrev === true ? -1 : (dirNext === true ? 1 : void 0)
632
+ if (tabVm.hasRouterLink === true) {
633
+ this.__verifyRouteModel()
634
+ }
635
+ }
636
+ },
572
637
 
573
- if (dir !== void 0) {
574
- const rtlDir = this.isRTL === true ? -1 : 1
575
- const index = tabs.indexOf(fromEl) + dir * rtlDir
638
+ /*
639
+ * Vue has an aggressive diff (in-place replacement) so we cannot
640
+ * ensure that the instance getting destroyed is the actual tab
641
+ * reported here. As a result, we cannot use its name or check
642
+ * if it's a route one to make the necessary updates. We need to
643
+ * always check the existing list again and infer the changes.
644
+ */
645
+ // used by children
646
+ __unregisterTab (tabVm) {
647
+ const index = this.tabVmList.indexOf(tabVm)
576
648
 
577
- if (
578
- index < 0 ||
579
- index >= tabsLength ||
580
- tabs[index].contains(document.activeElement) === true
581
- ) {
582
- return false
583
- }
649
+ this.tabVmList.splice(index, 1)
650
+ this.tabNameList.splice(index, 1)
584
651
 
585
- this.__scrollToTab(tabs[index], dir === rtlDir)
586
- this.__recalculateScroll()
652
+ this.__recalculateScroll()
587
653
 
588
- return true
654
+ // if we're watching route and this tab is a QRouteTab
655
+ if (this.unwatchRoute !== void 0 && tabVm.hasRouterLink !== void 0) {
656
+ // unwatch route if we don't have any QRouteTabs left
657
+ if (this.tabVmList.every(tab => tab.hasRouterLink === void 0) === true) {
658
+ this.unwatchRoute()
659
+ }
660
+
661
+ // then update model
662
+ this.__verifyRouteModel()
589
663
  }
664
+ },
665
+
666
+ __cleanup () {
667
+ clearTimeout(this.animateTimer)
668
+ this.__stopAnimScroll()
669
+ this.unwatchRoute !== void 0 && this.unwatchRoute()
590
670
  }
591
671
  },
592
672
 
593
673
  created () {
594
- this.buffer = []
674
+ this.__useTick('__registerScrollTick')
675
+ this.__useTick('__registerUpdateArrowsTick')
676
+ this.__useTick('__registerAnimateTick')
595
677
 
596
- this.__updateArrows = this.arrowsEnabled === true
597
- ? this.__updateArrowsFn
598
- : noop
599
- },
678
+ this.__useTimeout('__registerFocusTimeout', '__removeFocusTimeout')
679
+ this.__useTimeout('__registerScrollToTabTimeout', '__removeScrollToTabTimeout')
600
680
 
601
- mounted () {
602
- this.rtlHasScrollBug = rtlHasScrollBug()
681
+ Object.assign(this, {
682
+ tabVmList: [],
683
+ __localUpdateArrows: this.arrowsEnabled === true
684
+ ? this.__updateArrowsFn
685
+ : noop
686
+ })
603
687
  },
604
688
 
605
689
  activated () {
606
- if (this.shouldActivate !== true) { return }
690
+ this.hadRouteWatcher === true && this.__watchRoute()
607
691
  this.__recalculateScroll()
608
692
  },
609
693
 
610
694
  deactivated () {
611
- this.shouldActivate = true
695
+ this.hadRouteWatcher = this.unwatchRoute !== void 0
696
+ this.__cleanup()
612
697
  },
613
698
 
614
699
  beforeDestroy () {
615
- clearTimeout(this.bufferTimer)
616
- clearTimeout(this.animateTimer)
700
+ this.__cleanup()
617
701
  },
618
702
 
619
703
  render (h) {
@@ -624,7 +708,6 @@ export default Vue.extend({
624
708
 
625
709
  h('div', {
626
710
  ref: 'content',
627
- staticClass: 'q-tabs__content row no-wrap items-center self-stretch hide-scrollbar relative-position',
628
711
  class: this.innerClass,
629
712
  on: this.arrowsEnabled === true ? cache(this, 'scroll', { scroll: this.__updateArrowsFn }) : void 0
630
713
  }, slot(this, 'default'))
@@ -632,34 +715,33 @@ export default Vue.extend({
632
715
 
633
716
  this.arrowsEnabled === true && child.push(
634
717
  h(QIcon, {
635
- staticClass: 'q-tabs__arrow q-tabs__arrow--start absolute q-tab__icon',
636
- class: this.startArrow === true ? '' : 'q-tabs__arrow--faded',
637
- props: { name: this.arrowIcons.start },
718
+ class: 'q-tabs__arrow q-tabs__arrow--start absolute q-tab__icon' +
719
+ (this.leftArrow === true ? '' : ' q-tabs__arrow--faded'),
720
+ props: { name: this.leftIcon || this.$q.iconSet.tabs[ this.vertical === true ? 'up' : 'left' ] },
638
721
  on: cache(this, 'onS', {
639
- mousedown: this.__scrollToStart,
640
- touchstart: this.__scrollToStart,
641
- mouseup: this.__stopAnimScroll,
642
- mouseleave: this.__stopAnimScroll,
643
- touchend: this.__stopAnimScroll
722
+ '&mousedown': this.__scrollToStart,
723
+ '&touchstart': this.__scrollToStart,
724
+ '&mouseup': this.__stopAnimScroll,
725
+ '&mouseleave': this.__stopAnimScroll,
726
+ '&touchend': this.__stopAnimScroll
644
727
  })
645
728
  }),
646
729
 
647
730
  h(QIcon, {
648
- staticClass: 'q-tabs__arrow q-tabs__arrow--end absolute q-tab__icon',
649
- class: this.endArrow === true ? '' : 'q-tabs__arrow--faded',
650
- props: { name: this.arrowIcons.end },
731
+ class: 'q-tabs__arrow q-tabs__arrow--end absolute q-tab__icon' +
732
+ (this.rightArrow === true ? '' : ' q-tabs__arrow--faded'),
733
+ props: { name: this.rightIcon || this.$q.iconSet.tabs[ this.vertical === true ? 'down' : 'right' ] },
651
734
  on: cache(this, 'onE', {
652
- mousedown: this.__scrollToEnd,
653
- touchstart: this.__scrollToEnd,
654
- mouseup: this.__stopAnimScroll,
655
- mouseleave: this.__stopAnimScroll,
656
- touchend: this.__stopAnimScroll
735
+ '&mousedown': this.__scrollToEnd,
736
+ '&touchstart': this.__scrollToEnd,
737
+ '&mouseup': this.__stopAnimScroll,
738
+ '&mouseleave': this.__stopAnimScroll,
739
+ '&touchend': this.__stopAnimScroll
657
740
  })
658
741
  })
659
742
  )
660
743
 
661
744
  return h('div', {
662
- staticClass: 'q-tabs row no-wrap items-center',
663
745
  class: this.classes,
664
746
  on: this.onEvents,
665
747
  attrs: { role: 'tablist' }