index.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464
  1. "use strict";
  2. exports.__esModule = true;
  3. exports["default"] = void 0;
  4. var _utils = require("../utils");
  5. var _raf = require("../utils/raf");
  6. var _event = require("../utils/event");
  7. var _touch = require("../mixins/touch");
  8. var _scroll = require("../utils/scroll");
  9. var _use = (0, _utils.use)('tabs'),
  10. sfc = _use[0],
  11. bem = _use[1];
  12. var tabBem = (0, _utils.use)('tab')[1];
  13. var _default = sfc({
  14. mixins: [_touch.TouchMixin],
  15. model: {
  16. prop: 'active'
  17. },
  18. props: {
  19. color: String,
  20. sticky: Boolean,
  21. animated: Boolean,
  22. offsetTop: Number,
  23. swipeable: Boolean,
  24. background: String,
  25. titleActiveColor: String,
  26. titleInactiveColor: String,
  27. ellipsis: {
  28. type: Boolean,
  29. "default": true
  30. },
  31. lazyRender: {
  32. type: Boolean,
  33. "default": true
  34. },
  35. lineWidth: {
  36. type: Number,
  37. "default": null
  38. },
  39. lineHeight: {
  40. type: Number,
  41. "default": null
  42. },
  43. active: {
  44. type: [Number, String],
  45. "default": 0
  46. },
  47. type: {
  48. type: String,
  49. "default": 'line'
  50. },
  51. duration: {
  52. type: Number,
  53. "default": 0.3
  54. },
  55. swipeThreshold: {
  56. type: Number,
  57. "default": 4
  58. }
  59. },
  60. data: function data() {
  61. return {
  62. tabs: [],
  63. position: '',
  64. curActive: null,
  65. lineStyle: {
  66. backgroundColor: this.color
  67. },
  68. events: {
  69. resize: false,
  70. sticky: false,
  71. swipeable: false
  72. }
  73. };
  74. },
  75. computed: {
  76. // whether the nav is scrollable
  77. scrollable: function scrollable() {
  78. return this.tabs.length > this.swipeThreshold || !this.ellipsis;
  79. },
  80. wrapStyle: function wrapStyle() {
  81. switch (this.position) {
  82. case 'top':
  83. return {
  84. top: this.offsetTop + 'px',
  85. position: 'fixed'
  86. };
  87. case 'bottom':
  88. return {
  89. top: 'auto',
  90. bottom: 0
  91. };
  92. default:
  93. return null;
  94. }
  95. },
  96. navStyle: function navStyle() {
  97. return {
  98. borderColor: this.color,
  99. background: this.background
  100. };
  101. },
  102. trackStyle: function trackStyle() {
  103. if (this.animated) {
  104. return {
  105. left: -1 * this.curActive * 100 + "%",
  106. transitionDuration: this.duration + "s"
  107. };
  108. }
  109. }
  110. },
  111. watch: {
  112. active: function active(val) {
  113. if (val !== this.curActive) {
  114. this.correctActive(val);
  115. }
  116. },
  117. color: function color() {
  118. this.setLine();
  119. },
  120. tabs: function tabs() {
  121. this.correctActive(this.curActive || this.active);
  122. this.scrollIntoView();
  123. this.setLine();
  124. },
  125. curActive: function curActive() {
  126. this.scrollIntoView();
  127. this.setLine(); // scroll to correct position
  128. if (this.position === 'top' || this.position === 'bottom') {
  129. (0, _scroll.setScrollTop)(window, (0, _scroll.getElementTop)(this.$el) - this.offsetTop);
  130. }
  131. },
  132. sticky: function sticky() {
  133. this.handlers(true);
  134. },
  135. swipeable: function swipeable() {
  136. this.handlers(true);
  137. }
  138. },
  139. mounted: function mounted() {
  140. this.onShow();
  141. },
  142. activated: function activated() {
  143. this.onShow();
  144. this.setLine();
  145. },
  146. deactivated: function deactivated() {
  147. this.handlers(false);
  148. },
  149. beforeDestroy: function beforeDestroy() {
  150. this.handlers(false);
  151. },
  152. methods: {
  153. onShow: function onShow() {
  154. var _this = this;
  155. this.$nextTick(function () {
  156. _this.inited = true;
  157. _this.handlers(true);
  158. _this.scrollIntoView(true);
  159. });
  160. },
  161. // whether to bind sticky listener
  162. handlers: function handlers(bind) {
  163. var events = this.events;
  164. var sticky = this.sticky && bind;
  165. var swipeable = this.swipeable && bind; // listen to window resize event
  166. if (events.resize !== bind) {
  167. events.resize = bind;
  168. (bind ? _event.on : _event.off)(window, 'resize', this.setLine, true);
  169. } // listen to scroll event
  170. if (events.sticky !== sticky) {
  171. events.sticky = sticky;
  172. this.scrollEl = this.scrollEl || (0, _scroll.getScrollEventTarget)(this.$el);
  173. (sticky ? _event.on : _event.off)(this.scrollEl, 'scroll', this.onScroll, true);
  174. this.onScroll();
  175. } // listen to touch event
  176. if (events.swipeable !== swipeable) {
  177. events.swipeable = swipeable;
  178. var content = this.$refs.content;
  179. var action = swipeable ? _event.on : _event.off;
  180. action(content, 'touchstart', this.touchStart);
  181. action(content, 'touchmove', this.touchMove);
  182. action(content, 'touchend', this.onTouchEnd);
  183. action(content, 'touchcancel', this.onTouchEnd);
  184. }
  185. },
  186. // watch swipe touch end
  187. onTouchEnd: function onTouchEnd() {
  188. var direction = this.direction,
  189. deltaX = this.deltaX,
  190. curActive = this.curActive;
  191. var minSwipeDistance = 50;
  192. /* istanbul ignore else */
  193. if (direction === 'horizontal' && this.offsetX >= minSwipeDistance) {
  194. /* istanbul ignore else */
  195. if (deltaX > 0 && curActive !== 0) {
  196. this.setCurActive(curActive - 1);
  197. } else if (deltaX < 0 && curActive !== this.tabs.length - 1) {
  198. this.setCurActive(curActive + 1);
  199. }
  200. }
  201. },
  202. // adjust tab position
  203. onScroll: function onScroll() {
  204. var scrollTop = (0, _scroll.getScrollTop)(window) + this.offsetTop;
  205. var elTopToPageTop = (0, _scroll.getElementTop)(this.$el);
  206. var elBottomToPageTop = elTopToPageTop + this.$el.offsetHeight - this.$refs.wrap.offsetHeight;
  207. if (scrollTop > elBottomToPageTop) {
  208. this.position = 'bottom';
  209. } else if (scrollTop > elTopToPageTop) {
  210. this.position = 'top';
  211. } else {
  212. this.position = '';
  213. }
  214. var scrollParams = {
  215. scrollTop: scrollTop,
  216. isFixed: this.position === 'top'
  217. };
  218. this.$emit('scroll', scrollParams);
  219. },
  220. // update nav bar style
  221. setLine: function setLine() {
  222. var _this2 = this;
  223. var shouldAnimate = this.inited;
  224. this.$nextTick(function () {
  225. var tabs = _this2.$refs.tabs;
  226. if (!tabs || !tabs[_this2.curActive] || _this2.type !== 'line') {
  227. return;
  228. }
  229. var tab = tabs[_this2.curActive];
  230. var lineWidth = _this2.lineWidth,
  231. lineHeight = _this2.lineHeight;
  232. var width = (0, _utils.isDef)(lineWidth) ? lineWidth : tab.offsetWidth / 2;
  233. var left = tab.offsetLeft + (tab.offsetWidth - width) / 2;
  234. var lineStyle = {
  235. width: width + "px",
  236. backgroundColor: _this2.color,
  237. transform: "translateX(" + left + "px)"
  238. };
  239. if (shouldAnimate) {
  240. lineStyle.transitionDuration = _this2.duration + "s";
  241. }
  242. if ((0, _utils.isDef)(lineHeight)) {
  243. var height = lineHeight + "px";
  244. lineStyle.height = height;
  245. lineStyle.borderRadius = height;
  246. }
  247. _this2.lineStyle = lineStyle;
  248. });
  249. },
  250. // correct the value of active
  251. correctActive: function correctActive(active) {
  252. active = +active;
  253. var exist = this.tabs.some(function (tab) {
  254. return tab.index === active;
  255. });
  256. var defaultActive = (this.tabs[0] || {}).index || 0;
  257. this.setCurActive(exist ? active : defaultActive);
  258. },
  259. setCurActive: function setCurActive(active) {
  260. active = this.findAvailableTab(active, active < this.curActive);
  261. if ((0, _utils.isDef)(active) && active !== this.curActive) {
  262. this.$emit('input', active);
  263. if (this.curActive !== null) {
  264. this.$emit('change', active, this.tabs[active].title);
  265. }
  266. this.curActive = active;
  267. }
  268. },
  269. findAvailableTab: function findAvailableTab(active, reverse) {
  270. var diff = reverse ? -1 : 1;
  271. var index = active;
  272. while (index >= 0 && index < this.tabs.length) {
  273. if (!this.tabs[index].disabled) {
  274. return index;
  275. }
  276. index += diff;
  277. }
  278. },
  279. // emit event when clicked
  280. onClick: function onClick(index) {
  281. var _this$tabs$index = this.tabs[index],
  282. title = _this$tabs$index.title,
  283. disabled = _this$tabs$index.disabled;
  284. if (disabled) {
  285. this.$emit('disabled', index, title);
  286. } else {
  287. this.setCurActive(index);
  288. this.$emit('click', index, title);
  289. }
  290. },
  291. // scroll active tab into view
  292. scrollIntoView: function scrollIntoView(immediate) {
  293. var tabs = this.$refs.tabs;
  294. if (!this.scrollable || !tabs || !tabs[this.curActive]) {
  295. return;
  296. }
  297. var nav = this.$refs.nav;
  298. var scrollLeft = nav.scrollLeft,
  299. navWidth = nav.offsetWidth;
  300. var _tabs$this$curActive = tabs[this.curActive],
  301. offsetLeft = _tabs$this$curActive.offsetLeft,
  302. tabWidth = _tabs$this$curActive.offsetWidth;
  303. this.scrollTo(nav, scrollLeft, offsetLeft - (navWidth - tabWidth) / 2, immediate);
  304. },
  305. // animate the scrollLeft of nav
  306. scrollTo: function scrollTo(el, from, to, immediate) {
  307. if (immediate) {
  308. el.scrollLeft += to - from;
  309. return;
  310. }
  311. var count = 0;
  312. var frames = Math.round(this.duration * 1000 / 16);
  313. var animate = function animate() {
  314. el.scrollLeft += (to - from) / frames;
  315. /* istanbul ignore next */
  316. if (++count < frames) {
  317. (0, _raf.raf)(animate);
  318. }
  319. };
  320. animate();
  321. },
  322. // render title slot of child tab
  323. renderTitle: function renderTitle(el, index) {
  324. var _this3 = this;
  325. this.$nextTick(function () {
  326. var title = _this3.$refs.title[index];
  327. title.parentNode.replaceChild(el, title);
  328. });
  329. },
  330. getTabStyle: function getTabStyle(item, index) {
  331. var style = {};
  332. var color = this.color;
  333. var active = index === this.curActive;
  334. var isCard = this.type === 'card'; // theme color
  335. if (color) {
  336. if (!item.disabled && isCard && !active) {
  337. style.color = color;
  338. }
  339. if (!item.disabled && isCard && active) {
  340. style.backgroundColor = color;
  341. }
  342. if (isCard) {
  343. style.borderColor = color;
  344. }
  345. }
  346. var titleColor = active ? this.titleActiveColor : this.titleInactiveColor;
  347. if (titleColor) {
  348. style.color = titleColor;
  349. }
  350. if (this.scrollable && this.ellipsis) {
  351. style.flexBasis = 88 / this.swipeThreshold + '%';
  352. }
  353. return style;
  354. }
  355. },
  356. render: function render(h) {
  357. var _this4 = this;
  358. var type = this.type,
  359. ellipsis = this.ellipsis,
  360. animated = this.animated,
  361. scrollable = this.scrollable;
  362. var Nav = this.tabs.map(function (tab, index) {
  363. return h("div", {
  364. "ref": "tabs",
  365. "refInFor": true,
  366. "class": tabBem({
  367. active: index === _this4.curActive,
  368. disabled: tab.disabled,
  369. complete: !ellipsis
  370. }),
  371. "style": _this4.getTabStyle(tab, index),
  372. "on": {
  373. "click": function click() {
  374. _this4.onClick(index);
  375. }
  376. }
  377. }, [h("span", {
  378. "ref": "title",
  379. "refInFor": true,
  380. "class": {
  381. 'van-ellipsis': ellipsis
  382. }
  383. }, [tab.title])]);
  384. });
  385. return h("div", {
  386. "class": bem([type])
  387. }, [h("div", {
  388. "ref": "wrap",
  389. "style": this.wrapStyle,
  390. "class": [bem('wrap', {
  391. scrollable: scrollable
  392. }), {
  393. 'van-hairline--top-bottom': type === 'line'
  394. }]
  395. }, [h("div", {
  396. "ref": "nav",
  397. "class": bem('nav', [type]),
  398. "style": this.navStyle
  399. }, [this.slots('nav-left'), type === 'line' && h("div", {
  400. "class": bem('line'),
  401. "style": this.lineStyle
  402. }), Nav, this.slots('nav-right')])]), h("div", {
  403. "ref": "content",
  404. "class": bem('content', {
  405. animated: animated
  406. })
  407. }, [animated ? h("div", {
  408. "class": bem('track'),
  409. "style": this.trackStyle
  410. }, [this.slots()]) : this.slots()])]);
  411. }
  412. });
  413. exports["default"] = _default;