Blame view

src/components/tabs/tabs.vue 13.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']">
be3fbd24   marxy   Tabs add scroll
6
7
8
9
10
                  <div ref="navWrap" :class="[prefixCls + '-nav-wrap', scrollable ? prefixCls + '-nav-scrollable' : '']" >
                      <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';
13261a8b   TabEnter   remove getStyle
29
      import { oneOf } 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
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
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
              },
              scrollPrev() {
                  const containerWidth = this.$refs.navScroll.offsetWidth;
                  const currentOffset = this.getCurrentScrollOffset();
  
                  if (!currentOffset) return;
  
                  let newOffset = currentOffset > containerWidth
                  ? currentOffset - containerWidth
                  : 0;
  
                  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
                  ? currentOffset + containerWidth
                  : (navWidth - containerWidth);
  
                  this.setOffset(newOffset);
              },
              getCurrentScrollOffset() {
                  const { navStyle } = this;
                  return navStyle.transform
                  ? Number(navStyle.transform.match(/translateX\(-(\d+(\.\d+)*)px\)/)[1])
                  : 0;
              },
              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();
17f52abf   梁灏   update Tabs
313
314
              }
          },
17f52abf   梁灏   update Tabs
315
          watch: {
30510c3d   梁灏   support Tabs
316
317
318
              value (val) {
                  this.activeKey = val;
              },
17f52abf   梁灏   update Tabs
319
320
              activeKey () {
                  this.updateBar();
0c5e01f1   梁灏   fixed #185
321
                  this.updateStatus();
67c9b1c8   梁灏   fixed #591
322
                  this.broadcast('Table', 'on-visible-change', true);
a730a849   梁灏   update Tabs
323
                  this.$nextTick(() => {
be3fbd24   marxy   Tabs add scroll
324
325
                      this.scrollToActiveTab();
                  });
17f52abf   梁灏   update Tabs
326
              }
c4eb5dcf   H   tabs组件导航区添加右侧slot...
327
          },
8e4f708f   梁灏   update Tabs
328
329
          mounted () {
              this.showSlot = this.$slots.extra !== undefined;
be3fbd24   marxy   Tabs add scroll
330
331
332
333
334
              this.observer = elementResizeDetectorMaker();
              this.observer.listenTo(this.$refs.navWrap, this.handleResize);
          },
          beforeDestroy() {
              this.observer.removeListener(this.$refs.navWrap, this.handleResize);
17f52abf   梁灏   update Tabs
335
          }
b0893113   jingsam   :art: add eslint
336
337
      };
  </script>