Blame view

src/components/tabs/tabs.vue 17.1 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"
bb6efbaa   Aresn   Update tabs.vue
10
                  @keydown.space.prevent="handleTabKeyboardSelect(false)"
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
50
51
          else return nextTab;
      };
  
      const focusFirst = (element, root) => {
          try {element.focus();}
2f40e7ea   Sergio Crisostomo   add eslint-disabl...
52
          catch(err) {} // eslint-disable-line no-empty
7be1069a   Sergio Crisostomo   Add tab navigatio...
53
54
55
56
57
58
59
60
61
  
          if (document.activeElement == element && element !== root) return true;
  
          const candidates = element.children;
          for (let candidate of candidates) {
              if (focusFirst(candidate, root)) return true;
          }
          return false;
      };
17f52abf   梁灏   update Tabs
62
  
871ed4d8   梁灏   init Tabs component
63
      export default {
30510c3d   梁灏   support Tabs
64
          name: 'Tabs',
67c9b1c8   梁灏   fixed #591
65
          mixins: [ Emitter ],
1f974700   Aresn   Tabs support rend...
66
          components: { Icon, Render },
17f52abf   梁灏   update Tabs
67
          props: {
30510c3d   梁灏   support Tabs
68
              value: {
17f52abf   梁灏   update Tabs
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
                  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
92
          data () {
17f52abf   梁灏   update Tabs
93
94
95
96
              return {
                  prefixCls: prefixCls,
                  navList: [],
                  barWidth: 0,
30510c3d   梁灏   support Tabs
97
                  barOffset: 0,
c4eb5dcf   H   tabs组件导航区添加右侧slot...
98
                  activeKey: this.value,
7be1069a   Sergio Crisostomo   Add tab navigatio...
99
                  focusedKey: this.value,
be3fbd24   marxy   Tabs add scroll
100
                  showSlot: false,
a730a849   梁灏   update Tabs
101
                  navStyle: {
be3fbd24   marxy   Tabs add scroll
102
103
                      transform: ''
                  },
7be1069a   Sergio Crisostomo   Add tab navigatio...
104
105
                  scrollable: false,
                  transitioning: false,
b0893113   jingsam   :art: add eslint
106
              };
17f52abf   梁灏   update Tabs
107
108
109
110
111
112
113
114
115
116
          },
          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
117
                  ];
17f52abf   梁灏   update Tabs
118
119
120
121
              },
              contentClasses () {
                  return [
                      `${prefixCls}-content`,
77bafb31   梁灏   update Tabs
122
123
124
                      {
                          [`${prefixCls}-content-animated`]: this.animated
                      }
b0893113   jingsam   :art: add eslint
125
                  ];
17f52abf   梁灏   update Tabs
126
127
128
129
              },
              barClasses () {
                  return [
                      `${prefixCls}-ink-bar`,
77bafb31   梁灏   update Tabs
130
131
132
                      {
                          [`${prefixCls}-ink-bar-animated`]: this.animated
                      }
b0893113   jingsam   :art: add eslint
133
                  ];
17f52abf   梁灏   update Tabs
134
135
              },
              contentStyle () {
30510c3d   梁灏   support Tabs
136
                  const x = this.navList.findIndex((nav) => nav.name === this.activeKey);
17f52abf   梁灏   update Tabs
137
138
139
140
141
142
                  const p = x === 0 ? '0%' : `-${x}00%`;
  
                  let style = {};
                  if (x > -1) {
                      style = {
                          transform: `translateX(${p}) translateZ(0px)`
b0893113   jingsam   :art: add eslint
143
                      };
17f52abf   梁灏   update Tabs
144
145
146
147
148
                  }
                  return style;
              },
              barStyle () {
                  let style = {
3ce6b446   Sergio Crisostomo   Use visibility in...
149
                      visibility: 'hidden',
77bafb31   梁灏   update Tabs
150
                      width: `${this.barWidth}px`
17f52abf   梁灏   update Tabs
151
                  };
e906d570   Aresn   Update tabs.vue
152
                  if (this.type === 'line') style.visibility = 'visible';
77bafb31   梁灏   update Tabs
153
154
155
156
157
                  if (this.animated) {
                      style.transform = `translate3d(${this.barOffset}px, 0px, 0px)`;
                  } else {
                      style.left = `${this.barOffset}px`;
                  }
17f52abf   梁灏   update Tabs
158
159
160
161
162
163
164
165
166
167
168
169
  
                  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...
170
                          labelType: typeof pane.label,
17f52abf   梁灏   update Tabs
171
172
                          label: pane.label,
                          icon: pane.icon || '',
30510c3d   梁灏   support Tabs
173
                          name: pane.currentName || index,
7a737482   梁灏   fixed #206
174
175
                          disabled: pane.disabled,
                          closable: pane.closable
17f52abf   梁灏   update Tabs
176
                      });
30510c3d   梁灏   support Tabs
177
                      if (!pane.currentName) pane.currentName = index;
17f52abf   梁灏   update Tabs
178
                      if (index === 0) {
30510c3d   梁灏   support Tabs
179
                          if (!this.activeKey) this.activeKey = pane.currentName || index;
17f52abf   梁灏   update Tabs
180
181
                      }
                  });
77bafb31   梁灏   update Tabs
182
                  this.updateStatus();
17f52abf   梁灏   update Tabs
183
184
185
186
                  this.updateBar();
              },
              updateBar () {
                  this.$nextTick(() => {
30510c3d   梁灏   support Tabs
187
                      const index = this.navList.findIndex((nav) => nav.name === this.activeKey);
2993f4ee   梁灏   update Tab
188
                      if (!this.$refs.nav) return;  // 页面销毁时,这里会报错,为了解决 #2100
30510c3d   梁灏   support Tabs
189
                      const prevTabs = this.$refs.nav.querySelectorAll(`.${prefixCls}-tab`);
17f52abf   梁灏   update Tabs
190
                      const tab = prevTabs[index];
bdfab3b9   梁灏   fixed #1842
191
                      this.barWidth = tab ? parseFloat(tab.offsetWidth) : 0;
17f52abf   梁灏   update Tabs
192
193
194
195
196
  
                      if (index > 0) {
                          let offset = 0;
                          const gutter = this.size === 'small' ? 0 : 16;
                          for (let i = 0; i < index; i++) {
75798f5b   erhuluanzi   getComputedStyle在...
197
                              offset += parseFloat(prevTabs[i].offsetWidth) + gutter;
17f52abf   梁灏   update Tabs
198
199
200
201
202
203
                          }
  
                          this.barOffset = offset;
                      } else {
                          this.barOffset = 0;
                      }
be3fbd24   marxy   Tabs add scroll
204
                      this.updateNavScroll();
17f52abf   梁灏   update Tabs
205
206
                  });
              },
77bafb31   梁灏   update Tabs
207
208
              updateStatus () {
                  const tabs = this.getTabs();
30510c3d   梁灏   support Tabs
209
                  tabs.forEach(tab => tab.show = (tab.currentName === this.activeKey) || this.animated);
77bafb31   梁灏   update Tabs
210
              },
17f52abf   梁灏   update Tabs
211
212
213
214
215
              tabCls (item) {
                  return [
                      `${prefixCls}-tab`,
                      {
                          [`${prefixCls}-tab-disabled`]: item.disabled,
7be1069a   Sergio Crisostomo   Add tab navigatio...
216
217
                          [`${prefixCls}-tab-active`]: item.name === this.activeKey,
                          [`${prefixCls}-tab-focused`]: item.name === this.focusedKey,
17f52abf   梁灏   update Tabs
218
                      }
b0893113   jingsam   :art: add eslint
219
                  ];
17f52abf   梁灏   update Tabs
220
221
              },
              handleChange (index) {
7be1069a   Sergio Crisostomo   Add tab navigatio...
222
223
224
225
226
                  if (this.transitioning) return;
  
                  this.transitioning = true;
                  setTimeout(() => this.transitioning = false, transitionTime);
  
17f52abf   梁灏   update Tabs
227
228
                  const nav = this.navList[index];
                  if (nav.disabled) return;
30510c3d   梁灏   support Tabs
229
230
231
                  this.activeKey = nav.name;
                  this.$emit('input', nav.name);
                  this.$emit('on-click', nav.name);
17f52abf   梁灏   update Tabs
232
              },
7be1069a   Sergio Crisostomo   Add tab navigatio...
233
234
235
236
237
238
              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;
              },
8b410220   Aresn   handleTabKeyboard...
239
240
              handleTabKeyboardSelect(init = false){
                  if (init) return;
38ab7442   Sergio Crisostomo   Correct logic for...
241
242
243
                  const focused = this.focusedKey || 0;
                  const index = this.navList.findIndex(({name}) => name === focused);
                  this.handleChange(index);
7be1069a   Sergio Crisostomo   Add tab navigatio...
244
              },
17f52abf   梁灏   update Tabs
245
246
247
              handleRemove (index) {
                  const tabs = this.getTabs();
                  const tab = tabs[index];
087ad37d   梁灏   update Tabs
248
                  tab.$destroy();
17f52abf   梁灏   update Tabs
249
  
30510c3d   梁灏   support Tabs
250
                  if (tab.currentName === this.activeKey) {
17f52abf   梁灏   update Tabs
251
252
253
254
255
256
257
258
                      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
259
                              activeKey = rightNoDisabledTabs[0].currentName;
17f52abf   梁灏   update Tabs
260
                          } else if (leftNoDisabledTabs.length) {
30510c3d   梁灏   support Tabs
261
                              activeKey = leftNoDisabledTabs[leftNoDisabledTabs.length - 1].currentName;
17f52abf   梁灏   update Tabs
262
                          } else {
30510c3d   梁灏   support Tabs
263
                              activeKey = newTabs[0].currentName;
17f52abf   梁灏   update Tabs
264
265
266
                          }
                      }
                      this.activeKey = activeKey;
087ad37d   梁灏   update Tabs
267
                      this.$emit('input', activeKey);
17f52abf   梁灏   update Tabs
268
                  }
30510c3d   梁灏   support Tabs
269
                  this.$emit('on-tab-remove', tab.currentName);
17f52abf   梁灏   update Tabs
270
                  this.updateNav();
7a737482   梁灏   fixed #206
271
272
273
274
275
276
277
278
279
280
281
              },
              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
282
283
284
285
286
287
288
289
              },
              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...
290
291
                      ? currentOffset - containerWidth
                      : 0;
be3fbd24   marxy   Tabs add scroll
292
293
294
295
296
297
298
299
300
301
  
                  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...
302
303
                      ? currentOffset + containerWidth
                      : (navWidth - containerWidth);
be3fbd24   marxy   Tabs add scroll
304
305
306
307
308
309
  
                  this.setOffset(newOffset);
              },
              getCurrentScrollOffset() {
                  const { navStyle } = this;
                  return navStyle.transform
a74be22e   Sergio Crisostomo   don't check DOM t...
310
311
                      ? Number(navStyle.transform.match(/translateX\(-(\d+(\.\d+)*)px\)/)[1])
                      : 0;
be3fbd24   marxy   Tabs add scroll
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
348
349
350
351
352
353
354
355
356
357
358
359
360
              },
              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
361
362
363
              },
              isInsideHiddenElement () {
                  let parentNode = this.$el.parentNode;
a74be22e   Sergio Crisostomo   don't check DOM t...
364
                  while(parentNode && parentNode !== document.body) {
a90d1a20   yangdan8   解决isInsideHiddenE...
365
                      if (parentNode.style && parentNode.style.display === 'none') {
79885751   Kang Cheng   fix issue #1846
366
367
368
369
370
                          return parentNode;
                      }
                      parentNode = parentNode.parentNode;
                  }
                  return false;
17f52abf   梁灏   update Tabs
371
372
              }
          },
17f52abf   梁灏   update Tabs
373
          watch: {
30510c3d   梁灏   support Tabs
374
375
              value (val) {
                  this.activeKey = val;
7be1069a   Sergio Crisostomo   Add tab navigatio...
376
                  this.focusedKey = val;
30510c3d   梁灏   support Tabs
377
              },
7be1069a   Sergio Crisostomo   Add tab navigatio...
378
379
              activeKey (val) {
                  this.focusedKey = val;
17f52abf   梁灏   update Tabs
380
                  this.updateBar();
0c5e01f1   梁灏   fixed #185
381
                  this.updateStatus();
67c9b1c8   梁灏   fixed #591
382
                  this.broadcast('Table', 'on-visible-change', true);
a730a849   梁灏   update Tabs
383
                  this.$nextTick(() => {
be3fbd24   marxy   Tabs add scroll
384
385
                      this.scrollToActiveTab();
                  });
38ab7442   Sergio Crisostomo   Correct logic for...
386
387
388
389
390
  
                  // update visibility
                  const nextIndex = Math.max(this.navList.findIndex(tab => tab.name === this.focusedKey), 0);
                  [...this.$refs.panes.children].forEach((el, i) => {
                      if (nextIndex === i) {
3ce6b446   Sergio Crisostomo   Use visibility in...
391
392
                          [...el.children].forEach(child => child.style.visibility = 'visible');
                          setTimeout(() => focusFirst(el, el), transitionTime);
38ab7442   Sergio Crisostomo   Correct logic for...
393
                      } else {
3ce6b446   Sergio Crisostomo   Use visibility in...
394
                          [...el.children].forEach(child => child.style.visibility = 'hidden');
38ab7442   Sergio Crisostomo   Correct logic for...
395
396
                      }
                  });
17f52abf   梁灏   update Tabs
397
              }
c4eb5dcf   H   tabs组件导航区添加右侧slot...
398
          },
8e4f708f   梁灏   update Tabs
399
400
          mounted () {
              this.showSlot = this.$slots.extra !== undefined;
be3fbd24   marxy   Tabs add scroll
401
402
              this.observer = elementResizeDetectorMaker();
              this.observer.listenTo(this.$refs.navWrap, this.handleResize);
79885751   Kang Cheng   fix issue #1846
403
404
405
  
              const hiddenParentNode = this.isInsideHiddenElement();
              if (hiddenParentNode) {
3be0aa12   Kang Cheng   Import custom Mut...
406
                  this.mutationObserver = new MutationObserver(() => {
79885751   Kang Cheng   fix issue #1846
407
408
                      if (hiddenParentNode.style.display !== 'none') {
                          this.updateBar();
3be0aa12   Kang Cheng   Import custom Mut...
409
                          this.mutationObserver.disconnect();
79885751   Kang Cheng   fix issue #1846
410
411
412
                      }
                  });
  
3be0aa12   Kang Cheng   Import custom Mut...
413
                  this.mutationObserver.observe(hiddenParentNode, { attributes: true, childList: true, characterData: true, attributeFilter: ['style'] });
79885751   Kang Cheng   fix issue #1846
414
              }
45dbc6fd   Sergio Crisostomo   Correct disabled ...
415
  
8b410220   Aresn   handleTabKeyboard...
416
              this.handleTabKeyboardSelect(true);
be3fbd24   marxy   Tabs add scroll
417
418
419
          },
          beforeDestroy() {
              this.observer.removeListener(this.$refs.navWrap, this.handleResize);
9b6f316e   梁灏   fixed Tabs bug
420
              if (this.mutationObserver) this.mutationObserver.disconnect();
17f52abf   梁灏   update Tabs
421
          }
b0893113   jingsam   :art: add eslint
422
423
      };
  </script>