background

Recently, the company is making a component library. I was assigned to the Dialog component and received the task. Originally, I wanted to use the CV method to “learn” from the dialog component on the market, but the UI student was too imaginative and wanted to fill the dialog with some strange things like this

such

And this…

I’ll write a bee myself

start

The idea is to give the component different styles by passing in different props values

This article refers to two basic components, Overlay and Icon. The code is not posted, and these parts can be implemented by themselves

Template section, the page layout is divided into three parts: header, middle part content, button area button

<template>
  <Overlay :show="visible">
    <div class="my-dialog-wrapper">
      <transition name="my-dialog-bounce">
        <div :class="classes">
          <Icon
            @click="handleAction('close')"
            v-if="showClose"
            name="extra"
            class="my-icon my-icon-quxiao my-dialog-box-close"
          />
          <div v-if="imageUrl" class="my-dialog-box-img">
            <img :src="imageUrl" alt="" />
          </div>
          <div v-if="avatar" class="my-dialog-box-avatar">
            <img :src="avatar" alt="" />
          </div>
          <div class="my-dialog-box-title">{{ title }}</div>
          <div class="my-dialog-box-content">{{ message }}</div>
          <div class="my-dialog-box-tags">
            <div
              v-for="(item, index) in tags"
              :key="`tag${index}`"
              class="my-dialog-box-tags-item"
            >
              {{ item }}
            </div>
          </div>
          <slot name="content"></slot>
          <div :class="buttonClasses">
            <div
              v-if="showCancelButton"
              @click="handleAction('cancel')"
              :class="cancelBtnCls"
            >
              {{ cancelText }}
            </div>
            <div
              v-if="showConfirmButton"
              @click="handleAction('confirm')"
              :class="confirmBtnCls"
            >
              {{ confirmText }}
            </div>
          </div>
        </div>
      </transition>
    </div>
  </Overlay>
</template>
Copy the code

Js part

Support to use v-model to control dialog hide and display. For details about the passed props modelValue, see the vue3 official documentation

<script>
import Overlay from ".. /overlay";
import Icon from ".. /icon";
import {
  defineComponent,
  computed,
  ref,
  watch,
} from "vue";
const prefixCls = "my-dialog";
export default defineComponent({
  name: "my-dialog".inheritAttrs: false.props: {
    avatar: {
      type: String.default: "",},imageUrl: {
      type: String.default: "",},theme: {
      type: String.default: "",},showConfirmButton: {
      type: Boolean.default: true,},showCancelButton: {
      type: Boolean.default: false,},buttonSize: {
      type: String.default: "normal",},confirmText: {
      type: String.default: "Confirm",},cancelText: {
      type: String.default: "Cancel",},message: {
      type: String.default: "",},tags: {
      type: Array.default: () = >[],},showClose: {
      type: Boolean.default: false,},modelValue: {
      type: null.default: false,},title: {
      type: String.default: "",}},components: {
    Overlay,
    Icon
  },
  emits: ["update:modelValue"."action"].setup(props, { emit }) {
    letvisible = ref(!! props.modelValue);const classes = computed(() = > {
      return [
        `${prefixCls}-box`,
        props.showClose ? `${prefixCls}-box-with-close` : "",
        props.imageUrl ? `${prefixCls}-box-no-image` : "",
        (props.buttonSize === "large"&& props.showCancelButton) || ! props.theme ?`${prefixCls}-box-no-padding-botm`
          : "",]; });const buttonClasses = computed(() = > {
      return props.theme === "round"
        ? [
            `${prefixCls}-box-btns`,
            props.buttonSize === "large" ? `${prefixCls}-box-btns-large` : ""] : [`${prefixCls}-box-btns-line`,
            props.buttonSize === "large" ? `${prefixCls}-box-btns-large` : "",]; });const confirmBtnCls = computed(() = > {
      return props.theme === "round"
        ? [
            `${prefixCls}-box-btns-normal`.`${prefixCls}-box-btns-normal-confirm`,
            props.buttonSize === "large"
              ? `${prefixCls}-box-btns-large-confirm`
              : ""] : ["line-button".`${prefixCls}-box-btns-line-confirm`];
    });

    const cancelBtnCls = computed(() = > {
      return props.theme === "round"
        ? [
            `${prefixCls}-box-btns-normal`.`${prefixCls}-box-btns-normal-cancel`,
            props.buttonSize === "large"
              ? `${prefixCls}-box-btns-large-cancel`
              : ""] : ["line-button".`${prefixCls}-box-btns-line-cancel`];
    });
    
    const closeDialog = () = > {
      if(! visible)return;
      visible.value = false;
      emit("update:modelValue", !props.modelValue);
    };

    const handleAction = (action) = > {
      emit('action',action);
      closeDialog();
    };

    / / listen v - model
    watch(() = > props.modelValue,(val) = >{
      visible.value = val
    })

    return{ visible, classes, buttonClasses, cancelBtnCls, confirmBtnCls, closeDialog, handleAction, }; }}); </script>Copy the code

CSS part is not said, you can customize CSS according to their own needs

use

At this point, we can import the components to use, passing in the props we defined

<my-dialog
    :title="My first Nuggets article."
    :buttonSize="large"
    :message="Give me a lot of advice."
    :theme="round"
    v-model="dialogShow"
    showClose
    showCancelButton
  >
Copy the code

A function call

Do we normally call dialogs like this, and how does this work in VuE3?

this.$dialog
    .confirm({
      title: this.title,
      message: this.content,
      theme: "round",
    })
    .catch(() = > {
      console.log("Cancel");
    });
Copy the code

Based on the component implementation above, we can create a div element on the page and mount our component to it by dynamically mounting the Component when we call the this.$dialog

Vue.extend is no longer supported in Vue3, and there is no constructor anymore. We use the official render function to implement it

import DialogConstructor from "./Dialog.vue";
import { h, render } from "vue";

const dialogInstance = new Map(a);const initInstance = (props, container) = > {
  / / generated vnode
  const vnode = h(DialogConstructor, props);
  render(vnode, container);
  / / a mount
  document.body.appendChild(container);

  // Returns the component instance
  return vnode.component;
};

const getContainer = () = > {
  return document.createElement("div");
};

const showDialog = (options) = > {
  
  options.onAction = (action) = > {
    const currentDialog = dialogInstance.get(vm);
    let resolve = action;

    if (options.callback) {
      options.callback(resolve, instance.proxy);
    } else {
      if (action === "cancel" || action === "close") {
        currentDialog.reject("cancel");
      } else{ currentDialog.resolve(resolve); }}};const container = getContainer(); // Get the component container

  const instance = initInstance(options, container);

  const vm = instance.proxy;

  for (const prop in options) {
    if (options.hasOwnProperty(prop) && !vm.$props.hasOwnProperty(prop)) {
      vm[prop] = options[prop];
    }
  }
  / / display the dialog
  vm.visible = true;

  return vm;
};
function Dialog(options) {
  let callback;
  if (typeof options === "string") {
    options = {
      message: options,
    };
  } else {
    callback = options.callback;
  }
  return new Promise((resolve, reject) = > {
    const vm = showDialog(options);

    dialogInstance.set(vm, {
      options,
      callback,
      resolve,
      reject,
    });
  });
}

Dialog.alert = (options) = > {
  return Dialog(Object.assign({}, options, { boxType: "alert" }));
};

Dialog.confirm = (options) = > {
  return Dialog(
    Object.assign({ showCancelButton: true }, options, { boxType: "confirm"})); }; Dialog.close =() = > {
  dialogInstance.forEach((_, vm) = > {
    vm.closeDialog();
  });
  dialogInstance.clear();
};

DialogConstructor.install = (app) = >{
  const { name } = DialogConstructor;
  app.component(name, DialogConstructor)
}
Dialog.Component = DialogConstructor;

// Register the component
Dialog.install = (app) = > {
  app.use(Dialog.Component);
  app.config.globalProperties.$dialog = Dialog;
};

export default Dialog;

Copy the code

complete

The above is just some of my output in the learning process, I hope to share with you, if there is a better plan, welcome to discuss ~