npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@snail-js/vue

v1.0.0

Published

Vue 3 component library: an icon set that complements Element Plus, Aliyun captcha, click-to-copy, a right-click context menu and a rotating 3D word cloud.

Readme

@snail-js/vue

官方文档 | Document

Vue 3 组件库:

  • 扩展的图标库
  • 阿里验证码组件、
  • 一键复制组件、
  • 右键菜单组件
  • 3D 词云组件

安装

pnpm add @snail-js/vue
# 或
npm install @snail-js/vue

vue >= 3.5.0 是 peer 依赖,需要由应用自己安装。@floating-ui/vue 是本包的直接依赖, 随包自动安装。

1.引入样式:

// main.ts
import "@snail-js/vue/style.css";

2.两种使用方式

按需引入(推荐,支持Tree-shaking)

<script setup lang="ts">
import { SClickCopy, SIcon, IconVariable } from "@snail-js/vue";
</script>

<template>
  <SIcon :icon="IconVariable" />
  <SClickCopy text="要复制的文本" @success="onCopied" />
</template>

全局注册

import { createApp } from "vue";
import SnailVue from "@snail-js/vue";
import "@snail-js/vue/style.css";

createApp(App).use(SnailVue).mount("#app");

插件注册来自一个静态组件表,因此只引入 SClickCopy 的应用不会因为 .use(SnailVue) 之外 的原因把整套图标打进产物。install 会注册 SIcon、70 个图标组件、SClickCopy、AliCaptcha、 SContextMenu、SWordCloud、SWordTag。

组件列表

| 组件 | 说明 | | --- | --- | | SIcon | 图标包装器,接受组件、已注册的名字或插槽内容 | | SIcon 图标集 | 70 个 Icon* 组件,同时导出 iconComponents 映射与 IconName 类型 | | SClickCopy | 点击复制,带成功/失败反馈与无障碍状态播报 | | AliCaptcha | 阿里云验证码,同时支持 PNVS 与验证码 2.0(V3) | | SPopUpMenu | 函数式右键菜单,@floating-ui/vue 定位,支持任意层级子菜单 | | SWordCloud | 3D 旋转立体词云 | | SWordTag | 词云中的单个词,可独立使用 |

为什么需要这个图标库

@element-plus/icons-vue 已经有 293 个图标,本包不重复它们,新增的都是 Element Plus 没有的图标

  • 表格/行列操作(IconAddColumnAfter、IconMergeCells、IconPageSize…)、
  • 变量IconVariable、二维码IconQRCode、签署IconSign…
  • 以蜗牛IconSnail*、微信IconWechat…

图标组件是互补的,可以混用 —— SIcon 接受任何组件,包括 Element Plus 的图标组件:

<script setup lang="ts">
// Element Plus 的图标(@element-plus/icons-vue 导出的就是 Edit 这个名字)
import { Edit } from "@element-plus/icons-vue";
import { SIcon, IconVariable } from "@snail-js/vue";
</script>

<template>
  <!-- 直接传组件即可 -->
  <SIcon :icon="Edit" />

  <!-- 本包中 Element Plus 没有的图标 -->
  <SIcon :icon="IconVariable" />

  <!-- 交给 Element Plus 的容器 -->
  <el-icon><SIcon :icon="Edit" /></el-icon>
</template>

SIcon 自身是一个普通 <i>,继承 color 与 font-size,不依赖 Element Plus:没有安装 Element Plus 的应用也能正常渲染本包的图标。

SClickCopy 一键复制

<SClickCopy
  text="订单号 A-1024"
  html="<b>订单号 A-1024</b>"
  label="复制订单号"
  success-message="已复制"
  @success="({ text }) => console.log(text)"
  @error="({ error }) => console.warn(error)"
/>
  • text 优先;没有 text 时读取默认插槽的文本,也可以用 source="slot" 显式指定。
  • html 存在且平台支持 ClipboardItem 时走富文本复制,失败也会退回纯文本,不会因为富文本不可用而复制不了。
  • navigator.clipboard 只存在于安全上下文,因此同时提供了 textarea +document.execCommand("copy") 兜底;两条路径都失败才会触发 error。
  • 通过模板 ref 可以拿到 copy() 主动复制:
const copyRef = ref<InstanceType<typeof SClickCopy>>();
await copyRef.value?.copy(); // true 表示真的写进剪贴板了

默认插槽是作用域插槽,作用域为 { state, label, copy }。在组件自己的根元素上使用可选的 is-overlay修饰类。

AliCaptcha 阿里云验证码

两个产品用 product 区分,两个 SDK 都不随包发布,都是运行时按需动态加载:

<!-- 号码认证服务(PNVS)图形验证码 -->
<AliCaptcha
  product="pnvs"
  captcha-id="你的 captchaId"
  script-src="/vendor/aliyun/ct4.js"
  @success="({ result }) => verify(result)"
  @error="(error) => console.warn(error.code, error.message)"
/>

<!-- 验证码 2.0(V3 架构) -->
<AliCaptcha
  product="captcha2"
  scene-id="你的场景 ID"
  prefix="你的身份标"
  @success="({ captchaVerifyParam }) => verify(captchaVerifyParam)"
/>
  • product="pnvs" 需要 scriptSrc:ct4.js 是阿里提供的验证码文件,在阿里官方验证码示例中下载,缺失时会抛出带code: "missing-script-src" 的可操作错误。
  • product="captcha2" 从 https://o.alicdn.com/captcha-frontend/aliyunCaptcha/AliyunCaptcha.js动态加载,并在插入 <script> 之前写入window.AliyunCaptchaConfig = { region, prefix }。回调是 V3 的 success(captchaVerifyParam) / fail(result),不是 V2 的captchaVerifyCallback。prefix 是控制台的身份标,不是资源基础路径。
  • 同一个页面上多个实例共享同一个脚本加载(模块级 promise + 引用计数),不会重复引入 ——阿里云文档明确禁止重复引入与重复初始化。
  • 通过 ref 暴露 show() / hide() / reset() / refresh() / instance,各自映射到当前产品真实支持的方法(见类型注释);事件为 success、fail、error、close、ready。
  • 模块级代码不触碰 window/document,只有在 onMounted 之后才会初始化,可以安全地在 SSR 中导入。

SPopUpMenu 右键菜单

import { SPopUpMenu } from "@snail-js/vue";

function onContextMenu(event: MouseEvent) {
  const menu = SPopUpMenu(
    { context: { id: 1 }, onError: (error) => console.warn(error) },
    [
      {
        label: "编辑",
        // 异步判定:解析完成前该项是**隐藏**的,不会先渲染出来再消失
        display: () => canEdit({ id: 1 }),
        command: async ({ id }) => {
          await api.save(id); // 命令可以是异步的,菜单会等待
        }
      },
      { label: "删除", danger: true, command: () => api.remove(1) }
    ],
    event // 传入事件即可在鼠标位置打开;不传则贴着当前焦点元素
  );

  // 需要时可以自己关掉
  menu.close();
}
  • 异步判定是 fail-closed 的:display 是函数时该项在解析完成前隐藏,enabled 是函数时在解析完成前禁用;判定抛错/拒绝时保持隐藏/禁用,并通过 onError 上报一次。
  • 命令是真正被 await 的:执行期间该项显示忙碌状态且不可重复触发;失败时菜单保持打开并上报错误,成功才关闭(除非 closeOnClick: false)。
  • 子菜单支持任意层级,鼠标悬停和键盘(→ / ←)都能打开与返回。
  • 定位使用 @floating-ui/vue:offset + flip + shift + size,配合 autoUpdate 在滚动、尺寸变化时重新定位,菜单被 teleport 到 body,size 会把过高的菜单限制成可滚动列表。
  • 通过 createContextMenu(options, items, event) 也可以使用新名字,两者是同一个函数。
  • 菜单打开时会记录焦点,关闭后焦点回到打开前的元素。

菜单项类型为 SPopUpMenuItemOptions:label、icon?、hoverColor?、display?、enabled?、command?、children?、separator?、danger?、disabled?、closeOnClick?、id?; 菜单选项为 SPopUpMenuOptions:width?、minWidth?、align?、context?、closeOnClick?、onError?、loadingText?、zIndex?、position?。全部类型都从包根导出。

SWordCloud 3D 词云

<SWordCloud
  :words="[
    { text: '合同', weight: 120 },
    { text: '模板', weight: 40 },
    '签署'
  ]"
  :radius="160"
  :base-font-size="16"
  :colors="['#409eff', '#67c23a']"
  :pause-on-hover="true"
  @word-click="({ text }) => console.log(text)"
/>
  • words 接受 string[] 或 { text, weight?, color? }[];旧名 hotWords 仍然可用(同时传入时以 words 为准并给出开发提示)。
  • 整个词云只有一个 requestAnimationFrame 循环,尺寸只在布局变化(ResizeObserver、字体加载完成、属性变化)时测量一次。
  • 尊重 prefers-reduced-motion(渲染静态词云),鼠标悬停暂停(pauseOnHover,默认 true)、元素离开视口暂停、标签页隐藏暂停,卸载时全部取消。
  • 颜色由词的下标决定,重新渲染不会重新洗牌;colors 可以整体替换调色板。

类型与可访问性

  • 所有组件的 props、emits、插槽作用域与命令式实例类型都从包根导出。
  • 复制组件使用 role="button" + tabindex,Enter/Space 可触发,反馈文本通过aria-live="polite" 播报,成功/失败状态不会销毁获得焦点的元素。
  • 右键菜单使用 role="menu" / role="menuitem" / aria-disabled / aria-haspopup /aria-expanded,支持方向键、Home/End、Enter/Space 与 Escape。

作者

  • mc.lee