button

1. The demand

  • There can be different levels.
  • It could be a link, it could be text
  • Can click, focus, mouse hover
  • You can change the size: large, medium and small
  • It can be disabled.
  • Can be loaded

2. Make button support @click@focus @mouseover

  1. Vue does automatic handling of events, which are uploaded to the root element of the component by default
  • ButtonDemo. Vue. Bind on. Will default to the root element of the Button component.
  • The trigger event is executed, but button.vue does nothing.
<template>
    <div>The Button sample</div>
    <h1>Example 1</h1>
    <div>
        <Button @click="onClick" @focus="onClick" @mouseover="onClick">hello</Button>
    </div>

</template>

<script lang="ts">
    import Button from '.. /lib/button.vue'
    export default {
        name: "ButtonDemo".components:{Button},
        setup(){
            const onClick = () = >{
                console.log('hi')}return {onClick}
        }
    }
</script>
Copy the code
  • Make button.vue’s div not inherit the onclick attribute of the parent component (buttonDemo.vue).
  • Let button.vue set inheritAttrs:false. Indicates that events are not inherited
// button.vue
<template>
    <div style="border:1px solid red" :size="size">
    <button>
        <slot></slot>
    </button>
    </div>
</template>

<script lang="ts">
    export default {
        name: "button".inheritAttrs:false ,//1. Don't let div inherit: By inheriting attributes, div no longer has an inherited event from the external importButton binding
     
    }
</script>
Copy the code
  1. You only want these attributes to be inherited; other tags don’t.
  • Using the $attrs
// button.vue
<template>
    <div style="border:1px solid red" :size="size">
    <button v-bind="$attrs">
        <slot></slot>
    </button>
    </div>
</template>
<script lang="ts">
    export default {
        name: "button".inheritAttrs:false ,//1. Don't let div inherit: By inheriting attributes, div no longer has an inherited event from the external importButton binding
     
    }
</script>
Copy the code
  • After $attrs is added, all attributes/methods take effect on the button
  1. What if I want div and button to inherit different attributes?
  • Use the context in setup. Whoever uses that property, binds that property.
  • Improvements: Use… rest
<template>
    <div style="border:1px solid red" :size="size">
    <button v-bind="rest">
        <slot></slot>
    </button>
    </div>
</template>

<script lang="ts">
    export default {
        name: "button".inheritAttrs:false ,//1. Don't let div inherit: By inheriting attributes, div no longer has an inherited event from the external importButton binding
        setup(props, context){
            const{size,... rest} =context.attrs// Rest is all but size
            return {size,rest}

        }
    }
</script>
Copy the code

summary

  • Vue 3 property binding
  • By default, all attributes are bound to the root element
  • Use inheritAttrs: false to unbind the default
  • Get all attributes using $attrs or context.attrs
  • Batch bind attributes using v-bind=”$attrs”
  • Const {size, level… XXX} = context.attrs separates attributes

3. Make button support theme property

  1. Parent component (ButtonDemo) passes theme=”button” to child component (button.vue)
  • ButtonDemo.vue
<div>
        <Button >hello</Button>
        <Button theme="button">hello</Button>
        <Button theme="link">hello</Button>
        <Button theme="text">hello</Button>
    </div>
Copy the code
  • button.vue
<template>
    <button calss="gulu-button" :class="`theme-${theme}`">
        <slot></slot>
    </button>

</template>

<script lang="ts">
    export default {
        name: "button".props: {theme: {type:String.default:'button'}}}</script>
Copy the code
  1. Add CSS styles according to class

UI library CSS considerations

  • Scoped cannot be used
    1. Because XXX in data-v-xxx may run differently each time
    2. You must output a stable class selector that is easy to override
  • Must prefix
    1. .button does not work, it is easily overridden by the user
    2. . Wanwan-button can, not easily overridden
    3. .theme-link No, easily overridden by users
    4. . Wanwan-theme-link Yes, not easily overridden
  1. Minimum impact principle of CSS

Your CSS must not affect library users

  • Add your own class to CSS (Wanwan)
// start with wanwan-, or contain wanwan-[class^="wanwan-"].[class*="wanwan-"] {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
  font-size: 16px; // Whyfont-family/ / answer the font-family: https://github.com/zenozeng/fonts.css/ - apple - system,"Noto Sans"."Helvetica Neue", Helvetica,
  "Nimbus Sans L", Arial, "Liberation Sans"."PingFang SC"."Hiragino Sans GB"."Noto Sans CJK SC"."Source Han Sans SC"."Source Han Sans CN"."Microsoft YaHei"."Wenquanyi Micro Hei"."WenQuanYi Zen Hei"."ST Heiti", SimHei, "WenQuanYi Zen Hei Sharp", sans-serif;

}
Copy the code

4. Make button support size

  1. The parent component passes size, the child component props receives it, calculates class using a computed property in setup, and sets button’s class.
  • buttonDemo.vue
<div>
        <Button >normal</Button>
        <Button size="big">dadada</Button>
        <Button size="small">small</Button>
    </div>
Copy the code
  • button.vue
<template>
    <button class="wanwan-button" :class="classes">
        <slot></slot>
    </button>

</template>

<script lang="ts">
    import {computed} from 'vue'
    export default {
        name: "button".props: {theme: {type:String.default:'button'
            },
            size: {type:String.default:"normal"}},setup(props){
            const {theme,size} = props
            const classes = computed(() = >{
                return{[`wanwan-theme-${theme}`]: theme,
                    [`wanwan-size-${size}`]: size
                }
            })
            return {classes}
        }
    }
</script>
Copy the code

Make button support the level attribute

The value of level is main, normal, and danger. The principle is the same as above

Make Button support Disabled

  • buttonDemo.vue
<div> <Button disabled> <Button theme="link" disabled> </Button> <Button theme="text" </Button> </div>Copy the code

Add the Disabled property to the child component

  • button
<template>
    <button class="wanwan-button" :class="classes" :disabled="disabled">
        <slot></slot>
    </button>

</template>

<script lang="ts">
    import {computed} from 'vue'
    export default {
        name: "button".props: {theme: {type:String.default:'button'
            },
            size: {type:String.default:"normal"
            },
            level: {type:String.default:"normal"
            },
            disabled: {type:Boolean.default:false}},setup(props){
            const {theme,size,level} = props
            const classes = computed(() = >{
                return{[`wanwan-theme-${theme}`]: theme,
                    [`wanwan-size-${size}`]: size,
                    [`wanwan-level-${level}`]: level,
                }
            })
            return {classes}
        }
    }
</script>
Copy the code
  • Because disabled is declared, the button does not automatically inherit the disabled property, so we need to bind it to the button :disabled=”disabled”
  • Set CSS styles in button depending on whether disabled is present

Let button support loading

  • buttonDemo.vue
 <Button loading>loading</Button>
Copy the code
  • button.vue
<template>
    <button class="wanwan-button"
            :class="classes"
            :disabled="disabled">
        <span  v-if="loading"class="wanwan-loadingIndicator"></span>
        <slot></slot>
    </button>

</template>

<script lang="ts">
    import {computed} from 'vue'
    export default {
        name: "button".props: {theme: {type:String.default:'button'
            },
            size: {type:String.default:"normal"
            },
            level: {type:String.default:"normal"
            },
            disabled: {type:Boolean.default:false
            },
            loading: {type:Boolean.default:false}},setup(props){
            const {theme,size,level} = props
            const classes = computed(() = >{
                return{[`wanwan-theme-${theme}`]: theme,
                    [`wanwan-size-${size}`]: size,
                    [`wanwan-level-${level}`]: level,
                }
            })
            return {classes}
        }
    }
</script>
Copy the code
  • Add a span as loading
  1. Loading animation:
    @keyframes wanwan-spin {
        0%{transform: rotate(0deg)}
        100%{transform: rotate(360deg)}}animation: wanwan-spin 1s infinite linear;
Copy the code