index.js 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313
  1. import { VantComponent } from '../common/component';
  2. import { touch } from '../mixins/touch';
  3. VantComponent({
  4. mixins: [touch],
  5. relation: {
  6. name: 'tab',
  7. type: 'descendant',
  8. linked: function linked(child) {
  9. this.child.push(child);
  10. this.updateTabs(this.data.tabs.concat(child.data));
  11. },
  12. unlinked: function unlinked(child) {
  13. var index = this.child.indexOf(child);
  14. var tabs = this.data.tabs;
  15. tabs.splice(index, 1);
  16. this.child.splice(index, 1);
  17. this.updateTabs(tabs);
  18. }
  19. },
  20. props: {
  21. color: String,
  22. lineWidth: {
  23. type: Number,
  24. value: -1
  25. },
  26. active: {
  27. type: Number,
  28. value: 0
  29. },
  30. type: {
  31. type: String,
  32. value: 'line'
  33. },
  34. border: {
  35. type: Boolean,
  36. value: true
  37. },
  38. duration: {
  39. type: Number,
  40. value: 0.3
  41. },
  42. zIndex: {
  43. type: Number,
  44. value: 1
  45. },
  46. swipeThreshold: {
  47. type: Number,
  48. value: 4
  49. },
  50. animated: Boolean,
  51. sticky: Boolean,
  52. offsetTop: {
  53. type: Number,
  54. value: 0
  55. },
  56. swipeable: Boolean,
  57. scrollTop: {
  58. type: Number,
  59. value: 0
  60. }
  61. },
  62. data: {
  63. tabs: [],
  64. lineStyle: '',
  65. scrollLeft: 0,
  66. scrollable: false,
  67. trackStyle: '',
  68. wrapStyle: '',
  69. position: ''
  70. },
  71. watch: {
  72. swipeThreshold: function swipeThreshold() {
  73. this.set({
  74. scrollable: this.child.length > this.data.swipeThreshold
  75. });
  76. },
  77. color: 'setLine',
  78. lineWidth: 'setLine',
  79. active: 'setActiveTab',
  80. animated: 'setTrack',
  81. scrollTop: 'onScroll',
  82. offsetTop: 'setWrapStyle'
  83. },
  84. beforeCreate: function beforeCreate() {
  85. this.child = [];
  86. },
  87. mounted: function mounted() {
  88. this.setLine();
  89. this.setTrack();
  90. this.scrollIntoView();
  91. },
  92. methods: {
  93. updateTabs: function updateTabs(tabs) {
  94. tabs = tabs || this.data.tabs;
  95. this.set({
  96. tabs: tabs,
  97. scrollable: tabs.length > this.data.swipeThreshold
  98. });
  99. this.setActiveTab();
  100. },
  101. trigger: function trigger(eventName, index) {
  102. this.$emit(eventName, {
  103. index: index,
  104. title: this.data.tabs[index].title
  105. });
  106. },
  107. onTap: function onTap(event) {
  108. var index = event.currentTarget.dataset.index;
  109. if (this.data.tabs[index].disabled) {
  110. this.trigger('disabled', index);
  111. } else {
  112. this.trigger('click', index);
  113. this.setActive(index);
  114. }
  115. },
  116. setActive: function setActive(active) {
  117. if (active !== this.data.active) {
  118. this.trigger('change', active);
  119. this.set({
  120. active: active
  121. });
  122. this.setActiveTab();
  123. }
  124. },
  125. setLine: function setLine() {
  126. var _this = this;
  127. if (this.data.type !== 'line') {
  128. return;
  129. }
  130. var _this$data = this.data,
  131. color = _this$data.color,
  132. active = _this$data.active,
  133. duration = _this$data.duration,
  134. lineWidth = _this$data.lineWidth;
  135. this.getRect('.van-tab', true).then(function (rects) {
  136. var rect = rects[active];
  137. var width = lineWidth !== -1 ? lineWidth : rect.width / 2;
  138. var left = rects.slice(0, active).reduce(function (prev, curr) {
  139. return prev + curr.width;
  140. }, 0);
  141. left += (rect.width - width) / 2;
  142. _this.set({
  143. lineStyle: "\n width: " + width + "px;\n background-color: " + color + ";\n -webkit-transform: translateX(" + left + "px);\n -webkit-transition-duration: " + duration + "s;\n transform: translateX(" + left + "px);\n transition-duration: " + duration + "s;\n "
  144. });
  145. });
  146. },
  147. setTrack: function setTrack() {
  148. var _this2 = this;
  149. var _this$data2 = this.data,
  150. animated = _this$data2.animated,
  151. active = _this$data2.active,
  152. duration = _this$data2.duration;
  153. if (!animated) return '';
  154. this.getRect('.van-tabs__content').then(function (rect) {
  155. var width = rect.width;
  156. _this2.set({
  157. trackStyle: "\n width: " + width * _this2.child.length + "px;\n left: " + -1 * active * width + "px;\n transition: left " + duration + "s;\n display: flex;\n "
  158. });
  159. _this2.setTabsProps({
  160. width: width,
  161. animated: animated
  162. });
  163. });
  164. },
  165. setTabsProps: function setTabsProps(props) {
  166. this.child.forEach(function (item) {
  167. item.set(props);
  168. });
  169. },
  170. setActiveTab: function setActiveTab() {
  171. var _this3 = this;
  172. this.child.forEach(function (item, index) {
  173. var data = {
  174. active: index === _this3.data.active
  175. };
  176. if (data.active) {
  177. data.inited = true;
  178. }
  179. if (data.active !== item.data.active) {
  180. item.set(data);
  181. }
  182. });
  183. this.set({}, function () {
  184. _this3.setLine();
  185. _this3.setTrack();
  186. _this3.scrollIntoView();
  187. });
  188. },
  189. // scroll active tab into view
  190. scrollIntoView: function scrollIntoView() {
  191. var _this4 = this;
  192. if (!this.data.scrollable) {
  193. return;
  194. }
  195. this.getRect('.van-tab', true).then(function (tabRects) {
  196. var tabRect = tabRects[_this4.data.active];
  197. var offsetLeft = tabRects.slice(0, _this4.data.active).reduce(function (prev, curr) {
  198. return prev + curr.width;
  199. }, 0);
  200. var tabWidth = tabRect.width;
  201. _this4.getRect('.van-tabs__nav').then(function (navRect) {
  202. var navWidth = navRect.width;
  203. _this4.set({
  204. scrollLeft: offsetLeft - (navWidth - tabWidth) / 2
  205. });
  206. });
  207. });
  208. },
  209. onTouchStart: function onTouchStart(event) {
  210. if (!this.data.swipeable) return;
  211. this.touchStart(event);
  212. },
  213. onTouchMove: function onTouchMove(event) {
  214. if (!this.data.swipeable) return;
  215. this.touchMove(event);
  216. },
  217. // watch swipe touch end
  218. onTouchEnd: function onTouchEnd() {
  219. if (!this.data.swipeable) return;
  220. var _this$data3 = this.data,
  221. active = _this$data3.active,
  222. tabs = _this$data3.tabs;
  223. var direction = this.direction,
  224. deltaX = this.deltaX,
  225. offsetX = this.offsetX;
  226. var minSwipeDistance = 50;
  227. if (direction === 'horizontal' && offsetX >= minSwipeDistance) {
  228. if (deltaX > 0 && active !== 0) {
  229. this.setActive(active - 1);
  230. } else if (deltaX < 0 && active !== tabs.length - 1) {
  231. this.setActive(active + 1);
  232. }
  233. }
  234. },
  235. setWrapStyle: function setWrapStyle() {
  236. var _this$data4 = this.data,
  237. offsetTop = _this$data4.offsetTop,
  238. position = _this$data4.position;
  239. var wrapStyle;
  240. switch (position) {
  241. case 'top':
  242. wrapStyle = "\n top: " + offsetTop + "px;\n position: fixed;\n ";
  243. break;
  244. case 'bottom':
  245. wrapStyle = "\n top: auto;\n bottom: 0;\n ";
  246. break;
  247. default:
  248. wrapStyle = '';
  249. } // cut down `set`
  250. if (wrapStyle === this.data.wrapStyle) return;
  251. this.set({
  252. wrapStyle: wrapStyle
  253. });
  254. },
  255. // adjust tab position
  256. onScroll: function onScroll(scrollTop) {
  257. var _this5 = this;
  258. if (!this.data.sticky) return;
  259. var offsetTop = this.data.offsetTop;
  260. this.getRect('.van-tabs').then(function (rect) {
  261. var top = rect.top,
  262. height = rect.height;
  263. _this5.getRect('.van-tabs__wrap').then(function (rect) {
  264. var wrapHeight = rect.height;
  265. var position = '';
  266. if (offsetTop > top + height - wrapHeight) {
  267. position = 'bottom';
  268. } else if (offsetTop > top) {
  269. position = 'top';
  270. }
  271. _this5.$emit('scroll', {
  272. scrollTop: scrollTop + offsetTop,
  273. isFixed: position === 'top'
  274. });
  275. if (position !== _this5.data.position) {
  276. _this5.set({
  277. position: position
  278. }, function () {
  279. _this5.setWrapStyle();
  280. });
  281. }
  282. });
  283. });
  284. }
  285. }
  286. });