Your Web News in One Place

Help Webnuz

Referal links:

Sign up for GreenGeeks web hosting
March 24, 2022 12:37 pm GMT

Build a message component in Vue3

Intro

in this case lets build a message component in Vue3, lets take a look how some famous UI frameworks, like how Element UI does it

from the demo we know what we need to do:

  1. using transition to show and close the message
  2. control the time of interval
  3. take user input and display in the message container
  4. the message should occur in the middle of screen
  5. messages will not overlap, knowing position of each message is necessary

above are some rough guess when I first time did it, lets walk through and examine

Basic Setup

first, if you need help with how Vue3 work, especially for understanding some customs for <script setup>, check this article of mine

in our case, lets build a message component that can satisfy our need:

message.success("this is a big success")message.warning("take a look at this warning")message.error("what a tragedy")

lets start a Vue3 project using Vue CLI:

npm init vue@latest Project name:  <your-project-name> Add TypeScript?  No / Yes Add JSX Support?  No / Yes Add Vue Router for Single Page Application development?  No / Yes Add Pinia for state management?  No / Yes Add Vitest for Unit testing?  No / Yes Add Cypress for both Unit and End-to-End testing?  No / Yes Add ESLint for code quality?  No / Yes Add Prettier for code formating?  No / YesScaffolding project in ./<your-project-name>...Done.

lets construct our structure directory like the following:

- src-- components // this components directory is for basic UI components, not for detailed components---- packages------ message -------- co-message.scss // style file-------- co-message.vue  // template file -------- index.js       // component entry file-------- instance.js    // instance file ------ components.js // import each component and export as them in specific names------ index.js // register component globally across te entire project -- views // views!!---- Sample------ index.vue // where we demo the message component

in this case we are going to use Sass as css extension for better flexibility

you can read more about how to config scss for Vue3 in its doc

we will first build the template Vue file:

<template>    <transition name="slide-fade">        <div class="message-container" v-show="visible">            <!-- content -->            <div class="message-content">                <!-- message icon -->                <div class="message-icon" v-if="config.icon">                    <i :class="config.icon"></i>                </div>                <span v-text="config.content" class="message-text"></span>                <div class="option" v-if="!config.close">                    <!-- manually close the message by clicking close icon -->                    <i class="meta-iconfont meta-Close" @click="onClose"></i>                </div>            </div>        </div>    </transition></template><script>import { reactive, toRefs } from 'vue';import './co-message.scss'; // import style hereexport default {    props: {        config: { type: Object, default: () => {} }, // configuration        remove: { type: Function, default: () => {} } // unmount callback    },    setup(props) {        const state = reactive({            visible: false        });        // open message        const onOpen = config => {            setTimeout(() => {                state.visible = true;            }, 10);            // remove message after duration             if (config.duration !== 0) {                setTimeout(() => {                    onClose();                }, config.duration);            }        };        onOpen(props.config);        // onClose event        const onClose = () => {            state.visible = false;            setTimeout(() => {                props.remove();            }, 200);        };        return {            ...toRefs(state),            onOpen,            onClose        };    }};</script>

after you setup the template file (can ignore about style file for now), you can setup the instance.js first, it is mainly instructions about how to create an Vue instance and append your component to Body in HTML

instance.js:

import { createApp } from 'vue';import COMessage from './co-message.vue';/** * Message instance operation * @param {*} cfg configuration */const createInstance = cfg => {    const config = cfg || {};        // create a container and set its class    let messageNode = document.createElement('div');    let attr = document.createAttribute('class');    attr.value = 'CO-message';    messageNode.setAttributeNode(attr);        // set a counter, when the next message happens, it will have a distance from the previous one    const height = 70; // height, play around     const messageList = document.getElementsByClassName('CO-message');    messageNode.style.top = `${messageList.length * height}px`;        // reset each message's distance (Top value) to the top     const resetMsgTop = () => {        for (let i = 0; i < messageList.length; i++) {            messageList[i].style.top = `${i * height}px`;        }    };    const handleRemove = () => {        app.unmount(messageNode);        document.body.removeChild(messageNode);        resetMsgTop();    };        // create a Vue instance and append to Body    const app = createApp(COMessage, {        config,                 // remove the element, close message and unmount and remove from DOM        remove() {            handleRemove();        }    });        // mount the instance and append to end of Body    app.vm = app.mount(messageNode);    document.body.appendChild(messageNode);    app.close = () => {        handleRemove();    };    return app;};export default createInstance;

then you need to use this instance instruction in the entry file as index.js:

import createInstance from './instance.js';/** * read, config and render Message * @param {Object} typeCfg  * @param {Object/String} cfg */function renderMsg(typeCfg = {}, cfg = '') {    // allow passing params, need to tell the type     const isContent = typeof cfg === 'string';    // piece together config and merge them    cfg = isContent        ? {                content: cfg          }        : cfg;    const config = Object.assign({}, typeCfg, cfg); // merge configuration    const {        type = 'text', // type of message        icon = '', // your icon        content = '', // content        immersive = false, // show immersive?         duration = 3000, // set the duration         close = false // showClose?     } = config;    // create instance    return createInstance({        type,        icon,        content,        immersive,        duration,        close    });}export default {    // purely info    text(cfg = '') {        const textCfg = {            type: 'text',            icon: ''        };        return renderMsg(textCfg, cfg);    },    // success ere    success(cfg = '') {        const successCfg = {            type: 'success',            icon: 'icon-success success'        };        return renderMsg(successCfg, cfg);    },    // warning here    warning(cfg = '') {        const warningCfg = {            type: 'warning',            icon: 'icon-warning warning'        };        return renderMsg(warningCfg, cfg);    },    // error here    error(cfg = '') {        const errorCfg = {            type: 'error',            icon: 'icon-error error'        };        return renderMsg(errorCfg, cfg);    }};

feel free to replace icon-success icon-warning and icon-error with other icons such as remix icons, Flaticon, etc.

well, then we need to register it globally and call it directly inside your js code, lets do that first:

there is a components.js file in the same level to message folder:

export { default as COMessage } from './message';

and an index.js file that registers all components:

import * as components from './components';export default {    install: Vue => {        Object.keys(components).forEach(key => {            Vue.component(key, components[key]);            if (key === 'COMessage') {                Vue.config.globalProperties.$message = components[key];            }        });    }};

after you have done the above job, time to beautify the component using scss:

message.scss:

.CO-message {    position: fixed;    top: 0;    left: 0;    width: 100%;    text-align: center;    -webkit-box-align: center;    -ms-flex-align: center;    align-items: center;    box-sizing: border-box;    z-index: 9999;    transform: translateZ(9999px);    padding-top: 28px;    pointer-events: none;    transition: top 0.4s ease;    .message-container {        .message-text {            font-family: Roboto, sans-serif;            font-style: normal;            font-weight: 500;            font-size: 14px;            line-height: 20px;        }        .message-icon {            display: inline-block;            i {                font-size: 18px;                font-weight: 400;                margin-top: -3px;                margin-right: 6px;                display: inline-block;                box-sizing: border-box;                vertical-align: middle;            }            .success {                color: #32b732;            }            .warning {                color: #f2b847;            }            .error {                color: #fb4e4b;            }            //             .ri-loader-5-fill {                display: inline-block;                animation: rotating 1s ease-in-out infinite;                -webkit-animation: rotating 1s ease-in-out infinite;                color: #449efb;            }            @keyframes rotating {                0% {                    -webkit-transform: rotate(0deg);                    transform: rotate(0deg);                }                100% {                    -webkit-transform: rotate(359deg);                    transform: rotate(359deg);                }            }        }        .message-content {            display: flex;            justify-content: center;            align-items: center;            padding: 16px;            height: 52px;            margin-left: 16px;            margin-right: 16px;            text-align: left;            line-height: 45px;            font-size: 14px;            font-weight: 400;            border-radius: 4px;            color: #595959;            box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);            background: #ffffff;            span {                pointer-events: none;                -moz-user-select: none;                -o-user-select: none;                -khtml-user-select: none;                -webkit-user-select: none;                -ms-user-select: none;                user-select: none;            }            .option {                display: inline-block;                pointer-events: all;                margin-left: auto;                i {                    font-size: 18px;                    font-weight: 400;                    margin-top: -3px;                    display: inline-block;                    box-sizing: border-box;                    vertical-align: middle;                    cursor: pointer;                    color: #d9d9d9;                    transition: color 0.2s ease;                    &:hover {                        color: #fb4e4b;                        transition: color 0.2s ease;                    }                }            }        }        .message-text-immersive {            color: #2f7cd4;            border: 1px solid #96d5ff;            background: #f0faff;        }        .message-success-immersive {            color: #209124;            border: 1px solid #81d17b;            background: #ebf7e9;        }        .message-warning-immersive {            color: #cc9331;            border: 1px solid #ffe49c;            background: #fffcf0;        }        .message-error-immersive {            color: #d43538;            border: 1px solid #ffa69e;            background: #fff3f0;        }    }    .slide-fade-enter-active {        transition: all 0.2s ease-out;    }    .slide-fade-leave-active {        transition: all 0.2s ease;    }    .slide-fade-enter-from,    .slide-fade-leave-to {        transform: translateY(-20px);        opacity: 0;    }}

in this case you have successfully done your job, lets use it in a demo single file component

<template><button @click="showSuccessMessage">click me to show success</button><button @click="showWarningMessage">click me to show warning</button><button @click="showErrorMessage">click me to show error</button></template><script setup>import {ref, computed, getCurrentInstance} from "vue";const { proxy } = getCurrentInstance(); const showErrorMessage = () => {    proxy.$message.error('this is an error message');};const showSuccessMessage = () => {    proxy.$message.success('this is a success message');};const showWarningMessage = () => {    proxy.$message.warning('this is a warning message');};</script>

and it should work here

Image description

a basic preview via Loom

I have a border Collie, so I call my own demo component library CollieUI, thats why you saw prefix as CO- once it is done will share the Github repo, thx for reading


Original Link: https://dev.to/zhangjt9317/build-a-message-component-in-vue3-4hde

Share this article:    Share on Facebook
View Full Article

Dev To

An online community for sharing and discovering great ideas, having debates, and making friends

More About this Source Visit Dev To