Blame view

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