Blame view

src/components/tabs/tabs.vue 16 KB
871ed4d8   梁灏   init Tabs component
1
  <template>
17f52abf   梁灏   update Tabs
2
3
      <div :class="classes">
          <div :class="[prefixCls + '-bar']">
be3fbd24   marxy   Tabs add scroll
4
              <div :class="[prefixCls + '-nav-right']" v-if="showSlot"><slot name="extra"></slot></div>
7be1069a   Sergio Crisostomo   Add tab navigatio...
5
6
7
8
9
              <div
                  :class="[prefixCls + '-nav-container']"
                  tabindex="0"
                  ref="navContainer"
                  @keydown="handleTabKeyNavigation"
7fa3f9ef   Aresn   prevent space to ...
10
                  @keydown.space.prevent="handleTabKeyboardSelect"
7be1069a   Sergio Crisostomo   Add tab navigatio...
11
              >
67a9c1cc   梁灏   update Tabs
12
                  <div ref="navWrap" :class="[prefixCls + '-nav-wrap', scrollable ? prefixCls + '-nav-scrollable' : '']">
be3fbd24   marxy   Tabs add scroll
13
14
15
16
                      <span :class="[prefixCls + '-nav-prev', scrollable ? '' : prefixCls + '-nav-scroll-disabled']" @click="scrollPrev"><Icon type="chevron-left"></Icon></span>
                      <span :class="[prefixCls + '-nav-next', scrollable ? '' : prefixCls + '-nav-scroll-disabled']" @click="scrollNext"><Icon type="chevron-right"></Icon></span>
                      <div ref="navScroll" :class="[prefixCls + '-nav-scroll']">
                          <div ref="nav" :class="[prefixCls + '-nav']" class="nav-text"  :style="navStyle">
17f52abf   梁灏   update Tabs
17
                              <div :class="barClasses" :style="barStyle"></div>
30510c3d   梁灏   support Tabs
18
                              <div :class="tabCls(item)" v-for="(item, index) in navList" @click="handleChange(index)">
17f52abf   梁灏   update Tabs
19
                                  <Icon v-if="item.icon !== ''" :type="item.icon"></Icon>
1f974700   Aresn   Tabs support rend...
20
21
                                  <Render v-if="item.labelType === 'function'" :render="item.label"></Render>
                                  <template v-else>{{ item.label }}</template>
30510c3d   梁灏   support Tabs
22
                                  <Icon v-if="showClose(item)" type="ios-close-empty" @click.native.stop="handleRemove(index)"></Icon>
17f52abf   梁灏   update Tabs
23
24
                              </div>
                          </div>
17f52abf   梁灏   update Tabs
25
26
27
28
                      </div>
                  </div>
              </div>
          </div>
7be1069a   Sergio Crisostomo   Add tab navigatio...
29
          <div :class="contentClasses" :style="contentStyle" ref="panes"><slot></slot></div>
17f52abf   梁灏   update Tabs
30
      </div>
871ed4d8   梁灏   init Tabs component
31
32
  </template>
  <script>
17f52abf   梁灏   update Tabs
33
      import Icon from '../icon/icon.vue';
55dbf62d   Aresn   update Tabs render
34
      import Render from '../base/render';
3be0aa12   Kang Cheng   Import custom Mut...
35
      import { oneOf, MutationObserver } from '../../utils/assist';
67c9b1c8   梁灏   fixed #591
36
      import Emitter from '../../mixins/emitter';
be3fbd24   marxy   Tabs add scroll
37
      import elementResizeDetectorMaker from 'element-resize-detector';
17f52abf   梁灏   update Tabs
38
39
  
      const prefixCls = 'ivu-tabs';
7be1069a   Sergio Crisostomo   Add tab navigatio...
40
41
42
43
44
45
      const transitionTime = 300; // from CSS
  
      const getNextTab = (list, activeKey, direction, countDisabledAlso) => {
          const currentIndex = list.findIndex(tab => tab.name === activeKey);
          const nextIndex = (currentIndex + direction + list.length) % list.length;
          const nextTab = list[nextIndex];
45dbc6fd   Sergio Crisostomo   Correct disabled ...
46
          if (nextTab.disabled) return getNextTab(list, nextTab.name, direction, countDisabledAlso);
7be1069a   Sergio Crisostomo   Add tab navigatio...
47
48
49
          else return nextTab;
      };
  
871ed4d8   梁灏   init Tabs component
50
      export default {
30510c3d   梁灏   support Tabs
51
          name: 'Tabs',
67c9b1c8   梁灏   fixed #591
52
          mixins: [ Emitter ],
1f974700   Aresn   Tabs support rend...
53
          components: { Icon, Render },
17f52abf   梁灏   update Tabs
54
          props: {
30510c3d   梁灏   support Tabs
55
              value: {
17f52abf   梁灏   update Tabs
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
                  type: [String, Number]
              },
              type: {
                  validator (value) {
                      return oneOf(value, ['line', 'card']);
                  },
                  default: 'line'
              },
              size: {
                  validator (value) {
                      return oneOf(value, ['small', 'default']);
                  },
                  default: 'default'
              },
              animated: {
                  type: Boolean,
                  default: true
              },
              closable: {
                  type: Boolean,
                  default: false
              }
          },
871ed4d8   梁灏   init Tabs component
79
          data () {
17f52abf   梁灏   update Tabs
80
81
82
83
              return {
                  prefixCls: prefixCls,
                  navList: [],
                  barWidth: 0,
30510c3d   梁灏   support Tabs
84
                  barOffset: 0,
c4eb5dcf   H   tabs组件导航区添加右侧slot...
85
                  activeKey: this.value,
7be1069a   Sergio Crisostomo   Add tab navigatio...
86
                  focusedKey: this.value,
be3fbd24   marxy   Tabs add scroll
87
                  showSlot: false,
a730a849   梁灏   update Tabs
88
                  navStyle: {
be3fbd24   marxy   Tabs add scroll
89
90
                      transform: ''
                  },
7be1069a   Sergio Crisostomo   Add tab navigatio...
91
92
                  scrollable: false,
                  transitioning: false,
b0893113   jingsam   :art: add eslint
93
              };
17f52abf   梁灏   update Tabs
94
95
96
97
98
99
100
101
102
103
          },
          computed: {
              classes () {
                  return [
                      `${prefixCls}`,
                      {
                          [`${prefixCls}-card`]: this.type === 'card',
                          [`${prefixCls}-mini`]: this.size === 'small' && this.type === 'line',
                          [`${prefixCls}-no-animation`]: !this.animated
                      }
b0893113   jingsam   :art: add eslint
104
                  ];
17f52abf   梁灏   update Tabs
105
106
107
108
              },
              contentClasses () {
                  return [
                      `${prefixCls}-content`,
77bafb31   梁灏   update Tabs
109
110
111
                      {
                          [`${prefixCls}-content-animated`]: this.animated
                      }
b0893113   jingsam   :art: add eslint
112
                  ];
17f52abf   梁灏   update Tabs
113
114
115
116
              },
              barClasses () {
                  return [
                      `${prefixCls}-ink-bar`,
77bafb31   梁灏   update Tabs
117
118
119
                      {
                          [`${prefixCls}-ink-bar-animated`]: this.animated
                      }
b0893113   jingsam   :art: add eslint
120
                  ];
17f52abf   梁灏   update Tabs
121
122
              },
              contentStyle () {
30510c3d   梁灏   support Tabs
123
                  const x = this.navList.findIndex((nav) => nav.name === this.activeKey);
17f52abf   梁灏   update Tabs
124
125
126
127
128
129
                  const p = x === 0 ? '0%' : `-${x}00%`;
  
                  let style = {};
                  if (x > -1) {
                      style = {
                          transform: `translateX(${p}) translateZ(0px)`
b0893113   jingsam   :art: add eslint
130
                      };
17f52abf   梁灏   update Tabs
131
132
133
134
135
136
                  }
                  return style;
              },
              barStyle () {
                  let style = {
                      display: 'none',
77bafb31   梁灏   update Tabs
137
                      width: `${this.barWidth}px`
17f52abf   梁灏   update Tabs
138
139
                  };
                  if (this.type === 'line') style.display = 'block';
77bafb31   梁灏   update Tabs
140
141
142
143
144
                  if (this.animated) {
                      style.transform = `translate3d(${this.barOffset}px, 0px, 0px)`;
                  } else {
                      style.left = `${this.barOffset}px`;
                  }
17f52abf   梁灏   update Tabs
145
146
147
148
149
150
151
152
153
154
155
156
  
                  return style;
              }
          },
          methods: {
              getTabs () {
                  return this.$children.filter(item => item.$options.name === 'TabPane');
              },
              updateNav () {
                  this.navList = [];
                  this.getTabs().forEach((pane, index) => {
                      this.navList.push({
1f974700   Aresn   Tabs support rend...
157
                          labelType: typeof pane.label,
17f52abf   梁灏   update Tabs
158
159
                          label: pane.label,
                          icon: pane.icon || '',
30510c3d   梁灏   support Tabs
160
                          name: pane.currentName || index,
7a737482   梁灏   fixed #206
161
162
                          disabled: pane.disabled,
                          closable: pane.closable
17f52abf   梁灏   update Tabs
163
                      });
30510c3d   梁灏   support Tabs
164
                      if (!pane.currentName) pane.currentName = index;
17f52abf   梁灏   update Tabs
165
                      if (index === 0) {
30510c3d   梁灏   support Tabs
166
                          if (!this.activeKey) this.activeKey = pane.currentName || index;
17f52abf   梁灏   update Tabs
167
168
                      }
                  });
77bafb31   梁灏   update Tabs
169
                  this.updateStatus();
17f52abf   梁灏   update Tabs
170
171
172
173
                  this.updateBar();
              },
              updateBar () {
                  this.$nextTick(() => {
30510c3d   梁灏   support Tabs
174
                      const index = this.navList.findIndex((nav) => nav.name === this.activeKey);
2993f4ee   梁灏   update Tab
175
                      if (!this.$refs.nav) return;  // 页面销毁时,这里会报错,为了解决 #2100
30510c3d   梁灏   support Tabs
176
                      const prevTabs = this.$refs.nav.querySelectorAll(`.${prefixCls}-tab`);
17f52abf   梁灏   update Tabs
177
                      const tab = prevTabs[index];
bdfab3b9   梁灏   fixed #1842
178
                      this.barWidth = tab ? parseFloat(tab.offsetWidth) : 0;
17f52abf   梁灏   update Tabs
179
180
181
182
183
  
                      if (index > 0) {
                          let offset = 0;
                          const gutter = this.size === 'small' ? 0 : 16;
                          for (let i = 0; i < index; i++) {
75798f5b   erhuluanzi   getComputedStyle在...
184
                              offset += parseFloat(prevTabs[i].offsetWidth) + gutter;
17f52abf   梁灏   update Tabs
185
186
187
188
189
190
                          }
  
                          this.barOffset = offset;
                      } else {
                          this.barOffset = 0;
                      }
be3fbd24   marxy   Tabs add scroll
191
                      this.updateNavScroll();
17f52abf   梁灏   update Tabs
192
193
                  });
              },
77bafb31   梁灏   update Tabs
194
195
              updateStatus () {
                  const tabs = this.getTabs();
30510c3d   梁灏   support Tabs
196
                  tabs.forEach(tab => tab.show = (tab.currentName === this.activeKey) || this.animated);
77bafb31   梁灏   update Tabs
197
              },
17f52abf   梁灏   update Tabs
198
199
200
201
202
              tabCls (item) {
                  return [
                      `${prefixCls}-tab`,
                      {
                          [`${prefixCls}-tab-disabled`]: item.disabled,
7be1069a   Sergio Crisostomo   Add tab navigatio...
203
204
                          [`${prefixCls}-tab-active`]: item.name === this.activeKey,
                          [`${prefixCls}-tab-focused`]: item.name === this.focusedKey,
17f52abf   梁灏   update Tabs
205
                      }
b0893113   jingsam   :art: add eslint
206
                  ];
17f52abf   梁灏   update Tabs
207
208
              },
              handleChange (index) {
7be1069a   Sergio Crisostomo   Add tab navigatio...
209
210
211
212
213
                  if (this.transitioning) return;
  
                  this.transitioning = true;
                  setTimeout(() => this.transitioning = false, transitionTime);
  
17f52abf   梁灏   update Tabs
214
215
                  const nav = this.navList[index];
                  if (nav.disabled) return;
30510c3d   梁灏   support Tabs
216
217
218
                  this.activeKey = nav.name;
                  this.$emit('input', nav.name);
                  this.$emit('on-click', nav.name);
17f52abf   梁灏   update Tabs
219
              },
7be1069a   Sergio Crisostomo   Add tab navigatio...
220
221
222
223
224
225
226
              handleTabKeyNavigation(e){
                  if (e.keyCode !== 37 && e.keyCode !== 39) return;
                  const direction = e.keyCode === 39 ? 1 : -1;
                  const nextTab = getNextTab(this.navList, this.focusedKey, direction);
                  this.focusedKey = nextTab.name;
              },
              handleTabKeyboardSelect(){
45dbc6fd   Sergio Crisostomo   Correct disabled ...
227
228
229
                  this.activeKey = this.focusedKey || 0;
                  const nextIndex = Math.max(this.navList.findIndex(tab => tab.name === this.focusedKey), 0);
  
d45e9196   梁灏   update Tabs keybo...
230
                  this.handleChange(nextIndex);
7be1069a   Sergio Crisostomo   Add tab navigatio...
231
              },
17f52abf   梁灏   update Tabs
232
233
234
              handleRemove (index) {
                  const tabs = this.getTabs();
                  const tab = tabs[index];
087ad37d   梁灏   update Tabs
235
                  tab.$destroy();
17f52abf   梁灏   update Tabs
236
  
30510c3d   梁灏   support Tabs
237
                  if (tab.currentName === this.activeKey) {
17f52abf   梁灏   update Tabs
238
239
240
241
242
243
244
245
                      const newTabs = this.getTabs();
                      let activeKey = -1;
  
                      if (newTabs.length) {
                          const leftNoDisabledTabs = tabs.filter((item, itemIndex) => !item.disabled && itemIndex < index);
                          const rightNoDisabledTabs = tabs.filter((item, itemIndex) => !item.disabled && itemIndex > index);
  
                          if (rightNoDisabledTabs.length) {
30510c3d   梁灏   support Tabs
246
                              activeKey = rightNoDisabledTabs[0].currentName;
17f52abf   梁灏   update Tabs
247
                          } else if (leftNoDisabledTabs.length) {
30510c3d   梁灏   support Tabs
248
                              activeKey = leftNoDisabledTabs[leftNoDisabledTabs.length - 1].currentName;
17f52abf   梁灏   update Tabs
249
                          } else {
30510c3d   梁灏   support Tabs
250
                              activeKey = newTabs[0].currentName;
17f52abf   梁灏   update Tabs
251
252
253
                          }
                      }
                      this.activeKey = activeKey;
087ad37d   梁灏   update Tabs
254
                      this.$emit('input', activeKey);
17f52abf   梁灏   update Tabs
255
                  }
30510c3d   梁灏   support Tabs
256
                  this.$emit('on-tab-remove', tab.currentName);
17f52abf   梁灏   update Tabs
257
                  this.updateNav();
7a737482   梁灏   fixed #206
258
259
260
261
262
263
264
265
266
267
268
              },
              showClose (item) {
                  if (this.type === 'card') {
                      if (item.closable !== null) {
                          return item.closable;
                      } else {
                          return this.closable;
                      }
                  } else {
                      return false;
                  }
be3fbd24   marxy   Tabs add scroll
269
270
271
272
273
274
275
276
              },
              scrollPrev() {
                  const containerWidth = this.$refs.navScroll.offsetWidth;
                  const currentOffset = this.getCurrentScrollOffset();
  
                  if (!currentOffset) return;
  
                  let newOffset = currentOffset > containerWidth
a74be22e   Sergio Crisostomo   don't check DOM t...
277
278
                      ? currentOffset - containerWidth
                      : 0;
be3fbd24   marxy   Tabs add scroll
279
280
281
282
283
284
285
286
287
288
  
                  this.setOffset(newOffset);
              },
              scrollNext() {
                  const navWidth = this.$refs.nav.offsetWidth;
                  const containerWidth = this.$refs.navScroll.offsetWidth;
                  const currentOffset = this.getCurrentScrollOffset();
                  if (navWidth - currentOffset <= containerWidth) return;
  
                  let newOffset = navWidth - currentOffset > containerWidth * 2
a74be22e   Sergio Crisostomo   don't check DOM t...
289
290
                      ? currentOffset + containerWidth
                      : (navWidth - containerWidth);
be3fbd24   marxy   Tabs add scroll
291
292
293
294
295
296
  
                  this.setOffset(newOffset);
              },
              getCurrentScrollOffset() {
                  const { navStyle } = this;
                  return navStyle.transform
a74be22e   Sergio Crisostomo   don't check DOM t...
297
298
                      ? Number(navStyle.transform.match(/translateX\(-(\d+(\.\d+)*)px\)/)[1])
                      : 0;
be3fbd24   marxy   Tabs add scroll
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
              },
              setOffset(value) {
                  this.navStyle.transform = `translateX(-${value}px)`;
              },
              scrollToActiveTab() {
                  if (!this.scrollable) return;
                  const nav = this.$refs.nav;
                  const activeTab = this.$el.querySelector(`.${prefixCls}-tab-active`);
                  if(!activeTab) return;
  
                  const navScroll = this.$refs.navScroll;
                  const activeTabBounding = activeTab.getBoundingClientRect();
                  const navScrollBounding = navScroll.getBoundingClientRect();
                  const navBounding = nav.getBoundingClientRect();
                  const currentOffset = this.getCurrentScrollOffset();
                  let newOffset = currentOffset;
  
                  if (navBounding.right < navScrollBounding.right) {
                      newOffset = nav.offsetWidth - navScrollBounding.width;
                  }
  
                  if (activeTabBounding.left < navScrollBounding.left) {
                      newOffset = currentOffset - (navScrollBounding.left - activeTabBounding.left);
                  }else if (activeTabBounding.right > navScrollBounding.right) {
                      newOffset = currentOffset + activeTabBounding.right - navScrollBounding.right;
                  }
  
                  if(currentOffset !== newOffset){
                      this.setOffset(Math.max(newOffset, 0));
                  }
              },
              updateNavScroll(){
                  const navWidth = this.$refs.nav.offsetWidth;
                  const containerWidth = this.$refs.navScroll.offsetWidth;
                  const currentOffset = this.getCurrentScrollOffset();
                  if (containerWidth < navWidth) {
                      this.scrollable = true;
                      if (navWidth - currentOffset < containerWidth) {
                          this.setOffset(navWidth - containerWidth);
                      }
                  } else {
                      this.scrollable = false;
                      if (currentOffset > 0) {
                          this.setOffset(0);
                      }
                  }
              },
              handleResize(){
                  this.updateNavScroll();
79885751   Kang Cheng   fix issue #1846
348
349
350
              },
              isInsideHiddenElement () {
                  let parentNode = this.$el.parentNode;
a74be22e   Sergio Crisostomo   don't check DOM t...
351
                  while(parentNode && parentNode !== document.body) {
a90d1a20   yangdan8   解决isInsideHiddenE...
352
                      if (parentNode.style && parentNode.style.display === 'none') {
79885751   Kang Cheng   fix issue #1846
353
354
355
356
357
                          return parentNode;
                      }
                      parentNode = parentNode.parentNode;
                  }
                  return false;
17f52abf   梁灏   update Tabs
358
359
              }
          },
17f52abf   梁灏   update Tabs
360
          watch: {
30510c3d   梁灏   support Tabs
361
362
              value (val) {
                  this.activeKey = val;
7be1069a   Sergio Crisostomo   Add tab navigatio...
363
                  this.focusedKey = val;
30510c3d   梁灏   support Tabs
364
              },
7be1069a   Sergio Crisostomo   Add tab navigatio...
365
366
              activeKey (val) {
                  this.focusedKey = val;
17f52abf   梁灏   update Tabs
367
                  this.updateBar();
0c5e01f1   梁灏   fixed #185
368
                  this.updateStatus();
67c9b1c8   梁灏   fixed #591
369
                  this.broadcast('Table', 'on-visible-change', true);
a730a849   梁灏   update Tabs
370
                  this.$nextTick(() => {
be3fbd24   marxy   Tabs add scroll
371
372
                      this.scrollToActiveTab();
                  });
17f52abf   梁灏   update Tabs
373
              }
c4eb5dcf   H   tabs组件导航区添加右侧slot...
374
          },
8e4f708f   梁灏   update Tabs
375
376
          mounted () {
              this.showSlot = this.$slots.extra !== undefined;
be3fbd24   marxy   Tabs add scroll
377
378
              this.observer = elementResizeDetectorMaker();
              this.observer.listenTo(this.$refs.navWrap, this.handleResize);
79885751   Kang Cheng   fix issue #1846
379
380
381
  
              const hiddenParentNode = this.isInsideHiddenElement();
              if (hiddenParentNode) {
3be0aa12   Kang Cheng   Import custom Mut...
382
                  this.mutationObserver = new MutationObserver(() => {
79885751   Kang Cheng   fix issue #1846
383
384
                      if (hiddenParentNode.style.display !== 'none') {
                          this.updateBar();
3be0aa12   Kang Cheng   Import custom Mut...
385
                          this.mutationObserver.disconnect();
79885751   Kang Cheng   fix issue #1846
386
387
388
                      }
                  });
  
3be0aa12   Kang Cheng   Import custom Mut...
389
                  this.mutationObserver.observe(hiddenParentNode, { attributes: true, childList: true, characterData: true, attributeFilter: ['style'] });
79885751   Kang Cheng   fix issue #1846
390
              }
be3fbd24   marxy   Tabs add scroll
391
392
393
          },
          beforeDestroy() {
              this.observer.removeListener(this.$refs.navWrap, this.handleResize);
9b6f316e   梁灏   fixed Tabs bug
394
              if (this.mutationObserver) this.mutationObserver.disconnect();
17f52abf   梁灏   update Tabs
395
          }
b0893113   jingsam   :art: add eslint
396
397
      };
  </script>