Stories

Detail Return Return

強化 Naive UI n-message-provider 的 to 屬性,實現自由指向 - Stories Detail

效果

20250928_094014.gif

使用示例

<script setup lang="ts">
import ToMessage from "**/NaiveMessage";

function a() {
  ToMessage("#a", "測試消息a", { duration: 0, closable: true });
}
function b() {
  ToMessage("#b", "測試消息b", { duration: 0, closable: true });
}
</script>

<template>
  <div style="display: flex">
    <div
      id="a"
      style="width: 200px; height: 200px; border: 1px solid; margin: 10px"
    >
      <button @click="a()" style="padding: 10px">a+</button>
    </div>
    <div
      id="b"
      style="width: 200px; height: 200px; border: 1px solid; margin: 10px"
    >
      <button @click="b()" style="padding: 10px">b+</button>
    </div>
  </div>
</template>

副作用提示

Ctrl + f 搜索 注意副作用

掛載 App.vue

<template>
  <NConfigProvider>
    <div id="to-message-app-box"></div>
  </NConfigProvider>
</template>

組件 */index.vue

<script lang="ts" setup>
import { ref } from "vue";
import NMessage from "./Message.vue";

const msg = ref<InstanceType<typeof NMessage>>();

defineExpose({ msg });
</script>

<template>
  <n-message-provider>
    <NMessage ref="msg" />
  </n-message-provider>
</template>

<style lang="scss">
// 注意副作用 - 這會改變目標元素樣式
.n-message-container-target {
  position: relative;
  .n-message-container {
    position: absolute;
  }
}
</style>

組件 */Message.vue

<script lang="ts" setup>
import { useMessage } from "naive-ui";

const message = useMessage();

defineExpose({
  create: message.create,
});
</script>

組件 */index.ts

import {
  MessageOptions,
  MessageProviderProps,
  MessageReactive,
} from "naive-ui";
import { createApp, h, VNodeChild } from "vue";
import NMessage from "./index.vue";
import { App } from "vue";

const getDom = (to?: string | HTMLElement) => {
  to = to || "body";
  if (typeof to == "string") return document.querySelector(to) as HTMLElement;
  return to as HTMLElement;
};

const addClass = (dom: HTMLElement) =>
  dom.classList.add("n-message-container-target");
const removeClass = (dom: HTMLElement) =>
  dom.classList.remove("n-message-container-target");

type Provider = {
  target: HTMLElement;
  count: number;
  ref: InstanceType<typeof NMessage>;
  app: App<Element>;
  appDom: HTMLElement;
};
const providers: Provider[] = [];

type ToMessageType = (
  props: string | MessageProviderProps,
  content: string | (() => VNodeChild),
  option?: MessageOptions
) => Promise<MessageReactive | undefined>;

const ToMessage: ToMessageType = (props, content, option = {}) => {
  const appBox = document.querySelector("#to-message-app-box") as HTMLElement;

  props = typeof props == "string" ? { to: props } : props;
  props.to = getDom(props.to);
  // 注意副作用 - 這會改變目標元素dom樹
  const target = props.to;

  return new Promise((resolve) => {
    const onAfterLeave = option.onAfterLeave;
    option.onAfterLeave = () => {
      const item = providers.find((v) => v.target == target)!;

      item.count--;
      if (item.count > 0) return;

      item.app.unmount();
      appBox.removeChild(item.appDom);
      removeClass(item.target);
      providers.splice(providers.indexOf(item), 1);

      onAfterLeave?.();
    };

    const item = providers.find((v) => v.target == target);

    if (item) {
      item.count++;
      requestAnimationFrame(() =>
        resolve(item.ref.msg?.create(content, option))
      );
    } else {
      addClass(target);

      const appDom = document.createElement("div");
      appBox.appendChild(appDom);

      let app: Provider["app"];
      let is = true;
      const getRef = (v: Provider["ref"]) => {
        if (!v || !is) return;
        is = false;
        providers.push({ target, appDom, count: 1, ref: v, app });
        requestAnimationFrame(() => resolve(v.msg?.create(content, option)));
      };
      const vnode = () => h(NMessage, { ...props, ref: (v: any) => getRef(v) });
      app = createApp(vnode);
      app.mount(appDom);
    }
  });
};
export default ToMessage;
user avatar honwhy Avatar alibabawenyujishu Avatar zaotalk Avatar front_yue Avatar razyliang Avatar yishidemeihao_5b9ce075877c9 Avatar shuirong1997 Avatar xiaolei_599661330c0cb Avatar zhulongxu Avatar nqbefgvs Avatar wmbuke Avatar youyoufei Avatar
Favorites 123 users favorite the story!
Favorites

Add a new Comments

Some HTML is okay.