Episode 11: Implementing a set of ui component libraries (tab switching components) for pc vue from scratch

Episode 11: Implementation from scratch (tab switching component)

Location of this episode:
Let's talk about the meaning of tab switching first. Whether it's a mobile phone or a pc, the screen size is limited, and the range of people's eyes is limited. People don't like the operation of'jump'when they see information, or we need to look up some knowledge points. After entering the website, it's reasonable to see some information that they don't need. Of course, we quit to continue searching, and sometimes some of the knowledge we want may be at the bottom of the website, but people have browsing habits, which requires that in the first sight of the area, as much as possible display'keywords'and'key information', tab is to solve the problem of how to'expand' the limited space.

The tab component is different from other components. It needs at least two components to cooperate with each other. Writing three components is very annoying. Writing only one component is too bad in both semantics and writing style. Referring to the design of element, we also use two components. Writing is different from a single component. That is, it involves communication between two components.

1: Demand Analysis

  1. It consists of two parts, the top part is the display of the title and the bottom part is the display content according to the selected state.
  2. The title should have a clear activation state
  3. For performance, content display cannot use v-if
  4. Components like this wrapped type are not allowed to interfere with any operation of the user, such as the. stop modifier.

The method of use should be as follows

I use cc-tab as the parent label of the package component
Cc-tab-panel labels for each presentation

 <cc-tab v-model="activeName">
      <cc-tab-pane label="1 Number" name="one">1 Content of No.</cc-tab-pane>
      <cc-tab-pane label="2 Number" name="two">2 Content of No.</cc-tab-pane>
      <cc-tab-pane label="3 Number" name="three">3 Content of No.</cc-tab-pane>
 </cc-tab>

Expected results:

2: Foundation Construction

vue-cc-ui/src/components/Tab/index.js

import Tab from './main/tab.vue'
import TabPane from './main/tab-pane.vue'

Tab.install = function(Vue) {
  Vue.component(Tab.name, Tab);
  Vue.component(TabPane.name, TabPane);
};

export default Tab

Container assembly
vue-cc-ui/src/components/Tab/main/tab.vue

<template>
  <div class="cc-tab" >
    // After all, there will be many tags. Of course, the semantics of ul li is the best.
    // For example, three headings, you use three div s, but using ul li requires four tags, both advantages and disadvantages.
    <ul class="cc-tab-nav" >
      <li v-for="item in navList" >
          //Label name
      </li>
     </ul>
     // Here's what's on display.
    <slot />
  </div>
</template>

vue-cc-ui/src/components/Tab/main/tab-pane.vue
Responsible only for displaying and supplying component parameters to containers

<template>
  <div>
// The content of the presentation is written directly in the label, so slot is enough.
    <slot></slot>
  </div>
</template>

The container component also receives parameters

  1. Label is also the label name shown by tab.
  2. name is the id of the tag when clicked.

Another reason for these two separate settings is that label can be duplicated because it is not the only identifier and the name is not duplicated.

props: {
    label: {
      type: String,
      required: true
    },
    name: {
      type: String,
      required: true
    }
  },

3: Basic Functions

First, let's make the navigation function and show the title.
Inside the parent container:

// Code Specification for Personal Comparisons
// The mounted and created hooks are placed at the bottom.
// Because he doesn't change very often, he's just responsible for starting the code.
// He should conform to a single duty and not allow specific logical judgement.
// The function that he starts, if it's about initialization, must start with'init'.
mounted() {
    this.initNav();
  }

initNav

initNav() {
// Responsible only for handling each item
      this.layer(item => {
        let result = {
          label: item.label,
          name: item.name,
          icon: item.icon
        };
        // Put it in our navigation array
        this.navList.push(result);
      });
    },
    // The principle is the same as map and reduce. 
    // Every step will be vomited to the user.
    layer(task) {
      this.$slots.default.map(item => task(item.componentInstance));
    }

Explain:

  1. this.$slots: Get an object of all slot elements in this parent container, for example, the content in v-slot:foo will be found in vm.$slots.foo, and the default attribute includes all nodes that are not included in the named slot, or the content in v-slot:default.
  2. Each item obtained by the above loop this.$slots.default is a'node element', why do you type', because this node is processed by vue, not a node in the traditional sense;
  3. CompoonentOptions: As the name implies, some configuration items of this component, such as events not received by listeners, tag tag name, propsData, and propsData contain the name and label we need, but he needs componentOptions.propsData.name to get the value.
  4. Component Instance: Component state, whose parameters on this component can get the value of props directly. For example, componentInstance.name will get the value of props. Why did it choose it? Because it only needs'. 'to get the value once, the programmer's nature

Above we get a summary of the configuration of a user's incoming subcomponent, which we can show in a loop.

<div class="cc-tab">
    <ul class="cc-tab-nav">
      <li v-for="item in navList"
          :key='item.name'
          // When
          :class="{ 'is-active':item.name === value }"
          // This click event informs the subcomponent who exactly displays it
          @click="handClick($event,item.name)"
          >
          // Label code layout is more comfortable for content like this.
        <template>
        // Show his label name
          {{item.label}}
        </template>
      </li>
    </ul>
    <slot />
  </div>

handClick, the click event is responsible for giving the user's operation to the parent. After all, we bind the v-model so we give an input event.
tab-click is a user-accepted event

 handClick(e, name) {
      this.$emit("input", name);
      this.$emit("tab-click", e);
      // Change options here require a macro task, otherwise there are bug s that show incorrectness when testing.
      setTimeout(() => this.initSeleced(), 0);
    },

InitSeled A Special Method of Choosing

// A sentence
 initSeleced() {
    // Using the loop function we defined earlier
    // item is each sub-component whose data is mapped, so it can be modified.
    // When the value of the subcomponent is the same as the activated name, the display of the component is activated.
      this.layer(item => (item.showItem = item.name == this.value));
    },

Subcomponents

<template>
// After all, it is possible for users to switch tab s over and over again. show is more efficient.
  <div v-show="showItem">
    <slot></slot>
  </div>
</template>

<script>
export default {
  name: "ccTabPane",
  props: {
    label: {
      type: String,
      required: true
    },
    name: {
      type: String,
      required: true
    },
    icon: {
      type: String
    }
  },
  data() {
    return {
    // The default, of course, is false, not displayed.
      showItem:false
    };
  }
};
</script>

Now we've written down the core functions, but don't forget the little details.
Initialization selection

  mounted() {
    this.initNav();
    // Initially, the tab bar should also be activated by the user
    this.initSeleced();
  }

4: Style Design

  1. Perfect styles, such as tab activation, animation activation
  2. Different styles and styles of tab
  3. Addition of icon

/vue-cc-ui/src/style/Tab.scss

@import './common/var.scss';
@import './common/mixin.scss';
@import './common/extend.scss';

@include b(tab) {
    @include brother(nav) {
    // The overall title layout is the horizontal layout without changing lines.
        display: flex;
        flex-wrap: nowrap;
        text-align: center;
        // Provide a light horizontal line
        border-bottom: 1px solid #eee;
        margin-bottom: 10px;
        &>li {
        // The main thing is the style of each label.
            cursor: pointer;
            display: flex;
            position: relative;
            align-items: center;
            border-bottom: none;
            background-color: white;
            padding: 10px 20px;
            transition: all 0.2s;
            &:hover {
            // Give me good feedback.
                transform: scale(0.8)
            };
                &::after {
                // This is the following selected horizontal line, usually zoomed to 0, when used again.
                    content: '';
                    position: absolute;
                    left: 6px;
                    bottom: 0;
                    right: 6px;
                    transform: scale(0);
                    transition: all 0.2s;
                }
            @include when(active) {
            // When activated, the font will change color and horizontal lines will appear.
                color: $--color-nomal;
                &::after {
                    border-bottom: 2px solid $--color-nomal;
                    transform: scale(1);
                }
            }
        }
    }
}

Add icon

// I just sketched it out.
<li v-for="item in navList"
          :key='item.name'
          :class="{ 'is-active':item.name === value }"
          @click="handClick($event,item.name)"
          >
          // Input name appears, otherwise it does not appear
        <ccIcon v-if="item.icon"
                :name='item.icon'
                // There is an activated color.
                // It can also be written here (item. name === value) | '409EFF'
                // But ternary here is more flexible, may change the default color in the future.
                :color="item.name === value?'#409EFF':''" 
                />
        <template>
          {{item.label}}
        </template>
      </li>

Other types of tab s, wrap tags around them

Design sketch:


Allow users to choose to find this style

<ul class="cc-tab-nav"
        :class="{ 'is-card':type=='card' }"
        >

Relevant styles should also be compatible

@include when(card) {
            &::after {
                display: none
            }
            &>li {
                border-bottom: none;
                border: 1px solid #eee;
                &:hover {
                    transform: scale(1)
                }
            };
            &>li+li {
                border-left: none
            };
            &>.is-active {
                border-bottom: none;
                &::after {
                    content: '';
                    position: absolute;
                    border-bottom: 2px solid white;
                    left: 0;
                    right: 0;
                    bottom: -1px;
                }
            };
            &>:nth-last-child(1) {
                border-top-right-radius: 7px;
            };
            &>:nth-child(1) {
                border-top-left-radius: 7px;
            };
        }

One of the techniques of writing above is the following paragraph.
Users may have only one tab, you may ask, only one thing to do is tab?? I can only say, how to play is your job, I am only responsible for the implementation.
So when there's only one item, you can't just bend his upper left corner, but also let his upper right corner have an arc.

// These two selectors solved the problem perfectly.
// There is only one time when it is both the first and the last.
&>:nth-last-child(1) {
    border-top-right-radius: 7px;
};
&>:nth-child(1) {
    border-top-left-radius: 7px;
};

So far, the function of tab has been completed. Generally speaking, this tab component is a better one to write in cc-ui component.

end
We will continue to study together, make progress together and realize our self-worth as soon as possible!!
In the next episode, I'm going to talk about the'scoring component', which is the one that chooses Xiaoxing. It's a very interesting component. I like it very much.

The github address of this ui set: github
Personal Technology Blog: link

Tags: Javascript Vue Mobile Attribute github

Posted on Sun, 11 Aug 2019 02:05:40 -0700 by iceangel89