@yeyue03/drop-btns 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,5 @@
1
+ # Vue 3 + TypeScript + Vite
2
+
3
+ This template should help get you started developing with Vue 3 and TypeScript in Vite. The template uses Vue 3 `<script setup>` SFCs, check out the [script setup docs](https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup) to learn more.
4
+
5
+ Learn more about the recommended Project Setup and IDE Support in the [Vue Docs TypeScript Guide](https://vuejs.org/guide/typescript/overview.html#project-setup).
@@ -0,0 +1,51 @@
1
+ declare const _default: typeof __VLS_export;
2
+ export default _default;
3
+ declare const __VLS_export: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
4
+ /** 主按钮显示文字 */
5
+ buttonText: {
6
+ type: StringConstructor;
7
+ default: string;
8
+ };
9
+ /**
10
+ * 下拉选项列表
11
+ * 每项格式: { label: string, value: any, icon?: string, disabled?: boolean }
12
+ */
13
+ options: {
14
+ type: ArrayConstructor;
15
+ default: () => never[];
16
+ };
17
+ /** 控制下拉菜单显示/隐藏,支持 v-model:visible */
18
+ visible: {
19
+ type: BooleanConstructor;
20
+ default: undefined;
21
+ };
22
+ }>, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
23
+ "update:visible": (...args: any[]) => void;
24
+ select: (...args: any[]) => void;
25
+ }, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
26
+ /** 主按钮显示文字 */
27
+ buttonText: {
28
+ type: StringConstructor;
29
+ default: string;
30
+ };
31
+ /**
32
+ * 下拉选项列表
33
+ * 每项格式: { label: string, value: any, icon?: string, disabled?: boolean }
34
+ */
35
+ options: {
36
+ type: ArrayConstructor;
37
+ default: () => never[];
38
+ };
39
+ /** 控制下拉菜单显示/隐藏,支持 v-model:visible */
40
+ visible: {
41
+ type: BooleanConstructor;
42
+ default: undefined;
43
+ };
44
+ }>> & Readonly<{
45
+ "onUpdate:visible"?: ((...args: any[]) => any) | undefined;
46
+ onSelect?: ((...args: any[]) => any) | undefined;
47
+ }>, {
48
+ buttonText: string;
49
+ options: unknown[];
50
+ visible: boolean;
51
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
package/dist/index.css ADDED
@@ -0,0 +1,2 @@
1
+ .dropdown-wrapper[data-v-353d17d7]{display:inline-block;position:relative}.dropdown-trigger[data-v-353d17d7]{color:#1f2937;cursor:pointer;background:#fff;border:1px solid #d1d5db;border-radius:8px;align-items:center;gap:6px;padding:8px 16px;font-size:14px;font-weight:500;transition:all .2s;display:inline-flex;box-shadow:0 1px 2px #0000000a}.dropdown-trigger[data-v-353d17d7]:hover,.dropdown-trigger.is-active[data-v-353d17d7]{color:#2563eb;border-color:#2563eb;box-shadow:0 2px 8px #2563eb26}.trigger-text[data-v-353d17d7]{line-height:1}.trigger-arrow[data-v-353d17d7]{width:12px;height:12px;transition:transform .2s}.trigger-arrow.is-open[data-v-353d17d7]{transform:rotate(180deg)}.dropdown-menu[data-v-353d17d7]{z-index:1000;background:#fff;border:1px solid #e5e7eb;border-radius:8px;width:max-content;margin:0;padding:6px 0;list-style:none;position:absolute;top:calc(100% + 6px);left:0;overflow:hidden;box-shadow:0 8px 24px #0000001a}.dropdown-item[data-v-353d17d7]{color:#374151;cursor:pointer;border-bottom:1px solid #f3f4f6;align-items:center;gap:8px;padding:8px 14px;font-size:14px;transition:all .15s;display:flex}.dropdown-item[data-v-353d17d7]:last-child{border-bottom:none}.dropdown-item[data-v-353d17d7]:hover{color:#2563eb;background:#eff6ff}.dropdown-item.is-disabled[data-v-353d17d7]{color:#9ca3af;cursor:not-allowed;background:0 0}.item-icon[data-v-353d17d7]{color:currentColor;width:16px;height:16px;display:inline-flex}.item-icon[data-v-353d17d7] svg{width:100%;height:100%}.item-label[data-v-353d17d7]{white-space:nowrap;flex:1}.dropdown-fade-enter-active[data-v-353d17d7],.dropdown-fade-leave-active[data-v-353d17d7]{transition:opacity .18s,transform .18s}.dropdown-fade-enter-from[data-v-353d17d7],.dropdown-fade-leave-to[data-v-353d17d7]{opacity:0;transform:translateY(-4px)}
2
+ /*$vite$:1*/
@@ -0,0 +1,3 @@
1
+ import DropdownButton from './components/DropdownButton.vue';
2
+ export { DropdownButton };
3
+ export default DropdownButton;
package/dist/index.js ADDED
@@ -0,0 +1,91 @@
1
+ import { Fragment as e, Transition as t, computed as n, createCommentVNode as r, createElementBlock as i, createElementVNode as a, createVNode as o, normalizeClass as s, onBeforeUnmount as c, onMounted as l, openBlock as u, ref as d, renderList as f, toDisplayString as p, watch as m, withCtx as h } from "vue";
2
+ //#region \0plugin-vue:export-helper
3
+ var g = (e, t) => {
4
+ let n = e.__vccOpts || e;
5
+ for (let [e, r] of t) n[e] = r;
6
+ return n;
7
+ }, _ = { class: "trigger-text" }, v = {
8
+ key: 0,
9
+ class: "dropdown-menu"
10
+ }, y = ["onClick"], b = ["innerHTML"], x = { class: "item-label" }, S = /*#__PURE__*/ g({
11
+ __name: "DropdownButton",
12
+ props: {
13
+ buttonText: {
14
+ type: String,
15
+ default: "操作"
16
+ },
17
+ options: {
18
+ type: Array,
19
+ default: () => []
20
+ },
21
+ visible: {
22
+ type: Boolean,
23
+ default: void 0
24
+ }
25
+ },
26
+ emits: ["update:visible", "select"],
27
+ setup(g, { emit: S }) {
28
+ let C = g, w = S, T = d(!1), E = d(null);
29
+ function D() {
30
+ C.visible === void 0 ? T.value = !1 : w("update:visible", !1);
31
+ }
32
+ let O = n(() => C.visible === void 0 ? T.value : C.visible);
33
+ function k() {
34
+ C.visible === void 0 && (T.value = !0);
35
+ }
36
+ function A() {
37
+ C.visible === void 0 && (T.value = !1);
38
+ }
39
+ function j() {
40
+ C.visible === void 0 ? T.value = !T.value : w("update:visible", !C.visible);
41
+ }
42
+ function M(e) {
43
+ E.value && !E.value.contains(e.target) && D();
44
+ }
45
+ function N(e) {
46
+ e.disabled || (w("select", e), D());
47
+ }
48
+ return l(() => {
49
+ document.addEventListener("click", M);
50
+ }), c(() => {
51
+ document.removeEventListener("click", M);
52
+ }), m(() => C.visible, (e) => {
53
+ e !== void 0 && (T.value = e);
54
+ }), (n, c) => (u(), i("div", {
55
+ class: "dropdown-wrapper",
56
+ ref_key: "wrapperRef",
57
+ ref: E,
58
+ onMouseenter: k,
59
+ onMouseleave: A
60
+ }, [a("button", {
61
+ class: s(["dropdown-trigger", { "is-active": O.value }]),
62
+ onClick: j
63
+ }, [a("span", _, p(g.buttonText) + " - 测试", 1), (u(), i("svg", {
64
+ class: s(["trigger-arrow", { "is-open": O.value }]),
65
+ viewBox: "0 0 12 12",
66
+ fill: "none",
67
+ xmlns: "http://www.w3.org/2000/svg"
68
+ }, [...c[0] ||= [a("path", {
69
+ d: "M2.5 4.5L6 8L9.5 4.5",
70
+ stroke: "currentColor",
71
+ "stroke-width": "1.5",
72
+ "stroke-linecap": "round",
73
+ "stroke-linejoin": "round"
74
+ }, null, -1)]], 2))], 2), o(t, { name: "dropdown-fade" }, {
75
+ default: h(() => [O.value ? (u(), i("ul", v, [(u(!0), i(e, null, f(g.options, (e, t) => (u(), i("li", {
76
+ key: e.value ?? t,
77
+ class: s(["dropdown-item", { "is-disabled": e.disabled }]),
78
+ onClick: (t) => N(e)
79
+ }, [e.icon ? (u(), i("span", {
80
+ key: 0,
81
+ class: "item-icon",
82
+ innerHTML: e.icon
83
+ }, null, 8, b)) : r("", !0), a("span", x, p(e.label), 1)], 10, y))), 128))])) : r("", !0)]),
84
+ _: 1
85
+ })], 544));
86
+ }
87
+ }, [["__scopeId", "data-v-353d17d7"]]), C = S;
88
+ //#endregion
89
+ export { S as DropdownButton, C as default };
90
+
91
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/components/DropdownButton.vue","../src/index.ts"],"sourcesContent":["<template>\n <div class=\"dropdown-wrapper\" ref=\"wrapperRef\" @mouseenter=\"handleEnter\" @mouseleave=\"handleLeave\">\n <!-- 主按钮 -->\n <button class=\"dropdown-trigger\" :class=\"{ 'is-active': isOpen }\" @click=\"handleTriggerClick\">\n <span class=\"trigger-text\">{{ buttonText }} - 测试</span>\n <svg class=\"trigger-arrow\" :class=\"{ 'is-open': isOpen }\" viewBox=\"0 0 12 12\" fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M2.5 4.5L6 8L9.5 4.5\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\"\n stroke-linejoin=\"round\" />\n </svg>\n </button>\n\n <!-- 下拉菜单 -->\n <Transition name=\"dropdown-fade\">\n <ul v-if=\"isOpen\" class=\"dropdown-menu\">\n <li v-for=\"(item, index) in options\" :key=\"item.value ?? index\" class=\"dropdown-item\"\n :class=\"{ 'is-disabled': item.disabled }\" @click=\"handleSelect(item)\">\n <span v-if=\"item.icon\" class=\"item-icon\" v-html=\"item.icon\"></span>\n <span class=\"item-label\">{{ item.label }}</span>\n </li>\n </ul>\n </Transition>\n </div>\n</template>\n\n<script setup>\nimport { ref, watch, computed, onMounted, onBeforeUnmount } from 'vue'\n\nconst props = defineProps({\n /** 主按钮显示文字 */\n buttonText: {\n type: String,\n default: '操作'\n },\n /**\n * 下拉选项列表\n * 每项格式: { label: string, value: any, icon?: string, disabled?: boolean }\n */\n options: {\n type: Array,\n default: () => []\n },\n /** 控制下拉菜单显示/隐藏,支持 v-model:visible */\n visible: {\n type: Boolean,\n default: undefined\n }\n})\n\nconst emit = defineEmits(['update:visible', 'select'])\n\n// 内部状态:当未传入 visible 时由 hover 自主控制\nconst innerOpen = ref(false)\n\n// 根元素引用,用于判断点击是否发生在组件外部\nconst wrapperRef = ref(null)\n\n// 统一关闭菜单(兼容受控 v-model:visible 与非受控模式)\nfunction closeMenu() {\n if (props.visible !== undefined) {\n emit('update:visible', false)\n } else {\n innerOpen.value = false\n }\n}\n\nconst isOpen = computed(() => {\n return props.visible !== undefined ? props.visible : innerOpen.value\n})\n\nfunction handleEnter() {\n if (props.visible === undefined) innerOpen.value = true\n}\n\nfunction handleLeave() {\n if (props.visible === undefined) innerOpen.value = false\n}\n\n// 点击主按钮切换展开/收起(同时兼容受控 v-model:visible 与非受控模式)\nfunction handleTriggerClick() {\n if (props.visible !== undefined) {\n emit('update:visible', !props.visible)\n } else {\n innerOpen.value = !innerOpen.value\n }\n}\n\n// 点击组件外部区域时收起下拉菜单\nfunction handleOutsideClick(event) {\n if (wrapperRef.value && !wrapperRef.value.contains(event.target)) {\n closeMenu()\n }\n}\n\nfunction handleSelect(item) {\n if (item.disabled) return\n emit('select', item)\n closeMenu()\n}\n\n// 监听全局点击,实现\"点击外部关闭\"\nonMounted(() => {\n document.addEventListener('click', handleOutsideClick)\n})\n\nonBeforeUnmount(() => {\n document.removeEventListener('click', handleOutsideClick)\n})\n\n// 外部通过 v-model:visible 控制时,保持同步\nwatch(\n () => props.visible,\n (val) => {\n if (val !== undefined) innerOpen.value = val\n }\n)\n</script>\n\n<style scoped>\n.dropdown-wrapper {\n position: relative;\n display: inline-block;\n}\n\n/* ---------- 主按钮 ---------- */\n.dropdown-trigger {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 8px 16px;\n font-size: 14px;\n font-weight: 500;\n color: #1f2937;\n background: #ffffff;\n border: 1px solid #d1d5db;\n border-radius: 8px;\n cursor: pointer;\n transition: all 0.2s ease;\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);\n}\n\n.dropdown-trigger:hover {\n color: #2563eb;\n border-color: #2563eb;\n box-shadow: 0 2px 8px rgba(37, 99, 235, 0.15);\n}\n\n.dropdown-trigger.is-active {\n color: #2563eb;\n border-color: #2563eb;\n box-shadow: 0 2px 8px rgba(37, 99, 235, 0.15);\n}\n\n.trigger-text {\n line-height: 1;\n}\n\n.trigger-arrow {\n width: 12px;\n height: 12px;\n transition: transform 0.2s ease;\n}\n\n.trigger-arrow.is-open {\n transform: rotate(180deg);\n}\n\n/* ---------- 下拉菜单 ---------- */\n.dropdown-menu {\n position: absolute;\n top: calc(100% + 6px);\n left: 0;\n width: max-content;\n margin: 0;\n padding: 6px 0;\n list-style: none;\n background: #ffffff;\n border: 1px solid #e5e7eb;\n border-radius: 8px;\n box-shadow: 0 8px 24px rgba(0, 0, 0, 0.1);\n z-index: 1000;\n overflow: hidden;\n}\n\n.dropdown-item {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 14px;\n font-size: 14px;\n color: #374151;\n cursor: pointer;\n transition: all 0.15s ease;\n border-bottom: 1px solid #f3f4f6;\n /* 底部细边框隔开 */\n}\n\n.dropdown-item:last-child {\n border-bottom: none;\n}\n\n.dropdown-item:hover {\n color: #2563eb;\n /* 文字高亮变蓝 */\n background: #eff6ff;\n}\n\n.dropdown-item.is-disabled {\n color: #9ca3af;\n cursor: not-allowed;\n background: transparent;\n}\n\n.item-icon {\n display: inline-flex;\n width: 16px;\n height: 16px;\n color: currentColor;\n}\n\n.item-icon :deep(svg) {\n width: 100%;\n height: 100%;\n}\n\n.item-label {\n flex: 1;\n white-space: nowrap;\n}\n\n/* ---------- 过渡动画 ---------- */\n.dropdown-fade-enter-active,\n.dropdown-fade-leave-active {\n transition: opacity 0.18s ease, transform 0.18s ease;\n}\n\n.dropdown-fade-enter-from,\n.dropdown-fade-leave-to {\n opacity: 0;\n transform: translateY(-4px);\n}\n</style>\n","import DropdownButton from './components/DropdownButton.vue'\r\n\r\n// 具名导出:支持按需引入(import { DropdownButton } from 'btn-group-test')\r\nexport {\r\n DropdownButton\r\n}\r\n\r\n// 默认导出:支持整体引入(import BtnGroupTest from 'btn-group-test')\r\nexport default DropdownButton"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;EA4BA,IAAM,IAAQ,GAqBR,IAAO,GAGP,IAAY,EAAI,EAAK,GAGrB,IAAa,EAAI,IAAI;EAG3B,SAAS,IAAY;GACnB,AAAI,EAAM,YAAY,KAAA,IAGpB,EAAU,QAAQ,KAFlB,EAAK,kBAAkB,EAAK;EAIhC;EAEA,IAAM,IAAS,QACN,EAAM,YAAY,KAAA,IAA4B,EAAU,QAA1B,EAAM,OAC5C;EAED,SAAS,IAAc;GACrB,AAAI,EAAM,YAAY,KAAA,MAAW,EAAU,QAAQ;EACrD;EAEA,SAAS,IAAc;GACrB,AAAI,EAAM,YAAY,KAAA,MAAW,EAAU,QAAQ;EACrD;EAGA,SAAS,IAAqB;GAC5B,AAAI,EAAM,YAAY,KAAA,IAGpB,EAAU,QAAQ,CAAC,EAAU,QAF7B,EAAK,kBAAkB,CAAC,EAAM,OAAO;EAIzC;EAGA,SAAS,EAAmB,GAAO;GACjC,AAAI,EAAW,SAAS,CAAC,EAAW,MAAM,SAAS,EAAM,MAAM,KAC7D,EAAU;EAEd;EAEA,SAAS,EAAa,GAAM;GACtB,EAAK,aACT,EAAK,UAAU,CAAI,GACnB,EAAU;EACZ;SAGA,QAAgB;GACd,SAAS,iBAAiB,SAAS,CAAkB;EACvD,CAAC,GAED,QAAsB;GACpB,SAAS,oBAAoB,SAAS,CAAkB;EAC1D,CAAC,GAGD,QACQ,EAAM,UACX,MAAQ;GACP,AAAI,MAAQ,KAAA,MAAW,EAAU,QAAQ;EAC3C,CACF,cAlHE,EAAA,GAAA,EAqBM,OAAA;GArBD,OAAM;GAAuB,SAAA;GAAJ,KAAI;GAAc,cAAY;GAAc,cAAY;EAEpF,GAAA,CAAA,EAOS,UAAA;GAPD,OAAK,EAAA,CAAC,oBAAkB,EAAA,aAAwB,EAAA,MAAM,CAAA,CAAA;GAAK,SAAO;EACxE,GAAA,CAAA,EAAuD,QAAvD,GAAuD,EAAzB,EAAA,UAAU,IAAG,SAAK,CAAA,IAChD,EAAA,GAAA,EAIM,OAAA;GAJD,OAAK,EAAA,CAAC,iBAAe,EAAA,WAAsB,EAAA,MAAM,CAAA,CAAA;GAAI,SAAQ;GAAY,MAAK;GACjF,OAAM;EACN,GAAA,CAAA,GAAA,AAAA,EAAA,OAAA,CAAA,EAC4B,QAAA;GADtB,GAAE;GAAuB,QAAO;GAAe,gBAAa;GAAM,kBAAe;GACrF,mBAAgB;EAKtB,GAAA,MAAA,EAAA,CAAA,CAAA,GAAA,CAAA,EAAA,GAAA,CAAA,GAAA,EAQa,GAAA,EARD,MAAK,gBAAe,GAAA;GAC9B,SAAA,QAMK,CANK,EAAA,SAAV,EAAA,GAAA,EAMK,MANL,GAMK,EALH,EAAA,EAAA,GAAA,EAIK,GAAA,MAAA,EAJuB,EAAA,UAAhB,GAAM,OAAlB,EAAA,GAAA,EAIK,MAAA;IAJiC,KAAK,EAAK,SAAS;IAAO,OAAK,EAAA,CAAC,iBAAe,EAAA,eAC1D,EAAK,SAAQ,CAAA,CAAA;IAAK,UAAK,MAAE,EAAa,CAAI;GACvD,GAAA,CAAA,EAAK,QAAjB,EAAA,GAAA,EAAmE,QAAA;;IAA5C,OAAM;IAAY,WAAQ,EAAK;GACtD,GAAA,MAAA,GAAA,CAAA,KAAA,EAAA,IAAA,EAAA,GAAA,EAAgD,QAAhD,GAAgD,EAApB,EAAK,KAAK,GAAA,CAAA,CAAA,GAAA,IAAA,CAAA;;;;wCCVhD,IAAe"}
@@ -0,0 +1,2 @@
1
+ (function(e,t){typeof exports==`object`&&typeof module<`u`?t(exports,require("vue")):typeof define==`function`&&define.amd?define([`exports`,`vue`],t):(e=typeof globalThis<`u`?globalThis:e||self,t(e.MyDropBtns={},e.Vue))})(this,function(e,t){Object.defineProperties(e,{__esModule:{value:!0},[Symbol.toStringTag]:{value:`Module`}});var n=(e,t)=>{let n=e.__vccOpts||e;for(let[e,r]of t)n[e]=r;return n},r={class:`trigger-text`},i={key:0,class:`dropdown-menu`},a=[`onClick`],o=[`innerHTML`],s={class:`item-label`},c=n({__name:`DropdownButton`,props:{buttonText:{type:String,default:`操作`},options:{type:Array,default:()=>[]},visible:{type:Boolean,default:void 0}},emits:[`update:visible`,`select`],setup(e,{emit:n}){let c=e,l=n,u=(0,t.ref)(!1),d=(0,t.ref)(null);function f(){c.visible===void 0?u.value=!1:l(`update:visible`,!1)}let p=(0,t.computed)(()=>c.visible===void 0?u.value:c.visible);function m(){c.visible===void 0&&(u.value=!0)}function h(){c.visible===void 0&&(u.value=!1)}function g(){c.visible===void 0?u.value=!u.value:l(`update:visible`,!c.visible)}function _(e){d.value&&!d.value.contains(e.target)&&f()}function v(e){e.disabled||(l(`select`,e),f())}return(0,t.onMounted)(()=>{document.addEventListener(`click`,_)}),(0,t.onBeforeUnmount)(()=>{document.removeEventListener(`click`,_)}),(0,t.watch)(()=>c.visible,e=>{e!==void 0&&(u.value=e)}),(n,c)=>((0,t.openBlock)(),(0,t.createElementBlock)(`div`,{class:`dropdown-wrapper`,ref_key:`wrapperRef`,ref:d,onMouseenter:m,onMouseleave:h},[(0,t.createElementVNode)(`button`,{class:(0,t.normalizeClass)([`dropdown-trigger`,{"is-active":p.value}]),onClick:g},[(0,t.createElementVNode)(`span`,r,(0,t.toDisplayString)(e.buttonText)+` - 测试`,1),((0,t.openBlock)(),(0,t.createElementBlock)(`svg`,{class:(0,t.normalizeClass)([`trigger-arrow`,{"is-open":p.value}]),viewBox:`0 0 12 12`,fill:`none`,xmlns:`http://www.w3.org/2000/svg`},[...c[0]||=[(0,t.createElementVNode)(`path`,{d:`M2.5 4.5L6 8L9.5 4.5`,stroke:`currentColor`,"stroke-width":`1.5`,"stroke-linecap":`round`,"stroke-linejoin":`round`},null,-1)]],2))],2),(0,t.createVNode)(t.Transition,{name:`dropdown-fade`},{default:(0,t.withCtx)(()=>[p.value?((0,t.openBlock)(),(0,t.createElementBlock)(`ul`,i,[((0,t.openBlock)(!0),(0,t.createElementBlock)(t.Fragment,null,(0,t.renderList)(e.options,(e,n)=>((0,t.openBlock)(),(0,t.createElementBlock)(`li`,{key:e.value??n,class:(0,t.normalizeClass)([`dropdown-item`,{"is-disabled":e.disabled}]),onClick:t=>v(e)},[e.icon?((0,t.openBlock)(),(0,t.createElementBlock)(`span`,{key:0,class:`item-icon`,innerHTML:e.icon},null,8,o)):(0,t.createCommentVNode)(``,!0),(0,t.createElementVNode)(`span`,s,(0,t.toDisplayString)(e.label),1)],10,a))),128))])):(0,t.createCommentVNode)(``,!0)]),_:1})],544))}},[[`__scopeId`,`data-v-353d17d7`]]),l=c;e.DropdownButton=c,e.default=l});
2
+ //# sourceMappingURL=index.umd.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.umd.cjs","names":[],"sources":["../src/components/DropdownButton.vue","../src/index.ts"],"sourcesContent":["<template>\n <div class=\"dropdown-wrapper\" ref=\"wrapperRef\" @mouseenter=\"handleEnter\" @mouseleave=\"handleLeave\">\n <!-- 主按钮 -->\n <button class=\"dropdown-trigger\" :class=\"{ 'is-active': isOpen }\" @click=\"handleTriggerClick\">\n <span class=\"trigger-text\">{{ buttonText }} - 测试</span>\n <svg class=\"trigger-arrow\" :class=\"{ 'is-open': isOpen }\" viewBox=\"0 0 12 12\" fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M2.5 4.5L6 8L9.5 4.5\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\"\n stroke-linejoin=\"round\" />\n </svg>\n </button>\n\n <!-- 下拉菜单 -->\n <Transition name=\"dropdown-fade\">\n <ul v-if=\"isOpen\" class=\"dropdown-menu\">\n <li v-for=\"(item, index) in options\" :key=\"item.value ?? index\" class=\"dropdown-item\"\n :class=\"{ 'is-disabled': item.disabled }\" @click=\"handleSelect(item)\">\n <span v-if=\"item.icon\" class=\"item-icon\" v-html=\"item.icon\"></span>\n <span class=\"item-label\">{{ item.label }}</span>\n </li>\n </ul>\n </Transition>\n </div>\n</template>\n\n<script setup>\nimport { ref, watch, computed, onMounted, onBeforeUnmount } from 'vue'\n\nconst props = defineProps({\n /** 主按钮显示文字 */\n buttonText: {\n type: String,\n default: '操作'\n },\n /**\n * 下拉选项列表\n * 每项格式: { label: string, value: any, icon?: string, disabled?: boolean }\n */\n options: {\n type: Array,\n default: () => []\n },\n /** 控制下拉菜单显示/隐藏,支持 v-model:visible */\n visible: {\n type: Boolean,\n default: undefined\n }\n})\n\nconst emit = defineEmits(['update:visible', 'select'])\n\n// 内部状态:当未传入 visible 时由 hover 自主控制\nconst innerOpen = ref(false)\n\n// 根元素引用,用于判断点击是否发生在组件外部\nconst wrapperRef = ref(null)\n\n// 统一关闭菜单(兼容受控 v-model:visible 与非受控模式)\nfunction closeMenu() {\n if (props.visible !== undefined) {\n emit('update:visible', false)\n } else {\n innerOpen.value = false\n }\n}\n\nconst isOpen = computed(() => {\n return props.visible !== undefined ? props.visible : innerOpen.value\n})\n\nfunction handleEnter() {\n if (props.visible === undefined) innerOpen.value = true\n}\n\nfunction handleLeave() {\n if (props.visible === undefined) innerOpen.value = false\n}\n\n// 点击主按钮切换展开/收起(同时兼容受控 v-model:visible 与非受控模式)\nfunction handleTriggerClick() {\n if (props.visible !== undefined) {\n emit('update:visible', !props.visible)\n } else {\n innerOpen.value = !innerOpen.value\n }\n}\n\n// 点击组件外部区域时收起下拉菜单\nfunction handleOutsideClick(event) {\n if (wrapperRef.value && !wrapperRef.value.contains(event.target)) {\n closeMenu()\n }\n}\n\nfunction handleSelect(item) {\n if (item.disabled) return\n emit('select', item)\n closeMenu()\n}\n\n// 监听全局点击,实现\"点击外部关闭\"\nonMounted(() => {\n document.addEventListener('click', handleOutsideClick)\n})\n\nonBeforeUnmount(() => {\n document.removeEventListener('click', handleOutsideClick)\n})\n\n// 外部通过 v-model:visible 控制时,保持同步\nwatch(\n () => props.visible,\n (val) => {\n if (val !== undefined) innerOpen.value = val\n }\n)\n</script>\n\n<style scoped>\n.dropdown-wrapper {\n position: relative;\n display: inline-block;\n}\n\n/* ---------- 主按钮 ---------- */\n.dropdown-trigger {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 8px 16px;\n font-size: 14px;\n font-weight: 500;\n color: #1f2937;\n background: #ffffff;\n border: 1px solid #d1d5db;\n border-radius: 8px;\n cursor: pointer;\n transition: all 0.2s ease;\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);\n}\n\n.dropdown-trigger:hover {\n color: #2563eb;\n border-color: #2563eb;\n box-shadow: 0 2px 8px rgba(37, 99, 235, 0.15);\n}\n\n.dropdown-trigger.is-active {\n color: #2563eb;\n border-color: #2563eb;\n box-shadow: 0 2px 8px rgba(37, 99, 235, 0.15);\n}\n\n.trigger-text {\n line-height: 1;\n}\n\n.trigger-arrow {\n width: 12px;\n height: 12px;\n transition: transform 0.2s ease;\n}\n\n.trigger-arrow.is-open {\n transform: rotate(180deg);\n}\n\n/* ---------- 下拉菜单 ---------- */\n.dropdown-menu {\n position: absolute;\n top: calc(100% + 6px);\n left: 0;\n width: max-content;\n margin: 0;\n padding: 6px 0;\n list-style: none;\n background: #ffffff;\n border: 1px solid #e5e7eb;\n border-radius: 8px;\n box-shadow: 0 8px 24px rgba(0, 0, 0, 0.1);\n z-index: 1000;\n overflow: hidden;\n}\n\n.dropdown-item {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 14px;\n font-size: 14px;\n color: #374151;\n cursor: pointer;\n transition: all 0.15s ease;\n border-bottom: 1px solid #f3f4f6;\n /* 底部细边框隔开 */\n}\n\n.dropdown-item:last-child {\n border-bottom: none;\n}\n\n.dropdown-item:hover {\n color: #2563eb;\n /* 文字高亮变蓝 */\n background: #eff6ff;\n}\n\n.dropdown-item.is-disabled {\n color: #9ca3af;\n cursor: not-allowed;\n background: transparent;\n}\n\n.item-icon {\n display: inline-flex;\n width: 16px;\n height: 16px;\n color: currentColor;\n}\n\n.item-icon :deep(svg) {\n width: 100%;\n height: 100%;\n}\n\n.item-label {\n flex: 1;\n white-space: nowrap;\n}\n\n/* ---------- 过渡动画 ---------- */\n.dropdown-fade-enter-active,\n.dropdown-fade-leave-active {\n transition: opacity 0.18s ease, transform 0.18s ease;\n}\n\n.dropdown-fade-enter-from,\n.dropdown-fade-leave-to {\n opacity: 0;\n transform: translateY(-4px);\n}\n</style>\n","import DropdownButton from './components/DropdownButton.vue'\r\n\r\n// 具名导出:支持按需引入(import { DropdownButton } from 'btn-group-test')\r\nexport {\r\n DropdownButton\r\n}\r\n\r\n// 默认导出:支持整体引入(import BtnGroupTest from 'btn-group-test')\r\nexport default DropdownButton"],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,45 @@
1
+ {
2
+ "name": "@yeyue03/drop-btns",
3
+ "version": "1.0.0",
4
+ "description": "Vue 3 dropdown buttons component",
5
+ "type": "module",
6
+ "main": "./dist/index.umd.cjs",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js",
13
+ "require": "./dist/index.umd.cjs"
14
+ },
15
+ "./style.css": "./dist/index.css"
16
+ },
17
+ "files": [
18
+ "dist"
19
+ ],
20
+ "sideEffects": [
21
+ "**/*.css"
22
+ ],
23
+ "publishConfig": {
24
+ "access": "public"
25
+ },
26
+ "license": "UNLICENSED",
27
+ "scripts": {
28
+ "dev": "vite",
29
+ "build": "vue-tsc -b && vite build && vue-tsc -p tsconfig.build.json",
30
+ "preview": "vite preview",
31
+ "prepublishOnly": "pnpm build"
32
+ },
33
+ "devDependencies": {
34
+ "@types/node": "^24.13.3",
35
+ "@vitejs/plugin-vue": "^6.0.8",
36
+ "@vue/tsconfig": "^0.9.1",
37
+ "typescript": "~6.0.2",
38
+ "vue": "^3.5.41",
39
+ "vite": "^8.2.2",
40
+ "vue-tsc": "^3.3.11"
41
+ },
42
+ "peerDependencies": {
43
+ "vue": "^3.5.41"
44
+ }
45
+ }