An Interest In:
Web News this Week
- April 2, 2024
- April 1, 2024
- March 31, 2024
- March 30, 2024
- March 29, 2024
- March 28, 2024
- March 27, 2024
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:
- using transition to show and close the message
- control the time of interval
- take user input and display in the message container
- the message should occur in the middle of screen
- 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
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
Dev To
An online community for sharing and discovering great ideas, having debates, and making friendsMore About this Source Visit Dev To