@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 +5 -0
- package/dist/components/DropdownButton.vue.d.ts +51 -0
- package/dist/index.css +2 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +91 -0
- package/dist/index.js.map +1 -0
- package/dist/index.umd.cjs +2 -0
- package/dist/index.umd.cjs.map +1 -0
- package/package.json +45 -0
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*/
|
package/dist/index.d.ts
ADDED
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":"usBA4BA,IAAM,EAAQ,EAqBR,EAAO,EAGP,GAAA,EAAY,EAAA,IAAA,CAAI,EAAK,EAGrB,GAAA,EAAa,EAAA,IAAA,CAAI,IAAI,EAG3B,SAAS,GAAY,CACf,EAAM,UAAY,IAAA,GAGpB,EAAU,MAAQ,GAFlB,EAAK,iBAAkB,EAAK,CAIhC,CAEA,IAAM,GAAA,EAAS,EAAA,SAAA,KACN,EAAM,UAAY,IAAA,GAA4B,EAAU,MAA1B,EAAM,OAC5C,EAED,SAAS,GAAc,CACjB,EAAM,UAAY,IAAA,KAAW,EAAU,MAAQ,GACrD,CAEA,SAAS,GAAc,CACjB,EAAM,UAAY,IAAA,KAAW,EAAU,MAAQ,GACrD,CAGA,SAAS,GAAqB,CACxB,EAAM,UAAY,IAAA,GAGpB,EAAU,MAAQ,CAAC,EAAU,MAF7B,EAAK,iBAAkB,CAAC,EAAM,OAAO,CAIzC,CAGA,SAAS,EAAmB,EAAO,CAC7B,EAAW,OAAS,CAAC,EAAW,MAAM,SAAS,EAAM,MAAM,GAC7D,EAAU,CAEd,CAEA,SAAS,EAAa,EAAM,CACtB,EAAK,WACT,EAAK,SAAU,CAAI,EACnB,EAAU,EACZ,QAGA,EAAA,EAAA,UAAA,KAAgB,CACd,SAAS,iBAAiB,QAAS,CAAkB,CACvD,CAAC,GAED,EAAA,EAAA,gBAAA,KAAsB,CACpB,SAAS,oBAAoB,QAAS,CAAkB,CAC1D,CAAC,GAGD,EAAA,EAAA,MAAA,KACQ,EAAM,QACX,GAAQ,CACH,IAAQ,IAAA,KAAW,EAAU,MAAQ,EAC3C,CACF,WAlHE,EAAA,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAqBM,MAAA,CArBD,MAAM,mBAAuB,QAAA,aAAJ,IAAI,EAAc,aAAY,EAAc,aAAY,CAEpF,EAAA,EAAA,EAAA,EAAA,mBAAA,CAOS,SAAA,CAPD,OAAA,EAAK,EAAA,eAAA,CAAA,CAAC,mBAAkB,CAAA,YAAwB,EAAA,KAAM,CAAA,CAAA,EAAK,QAAO,CACxE,EAAA,EAAA,EAAA,EAAA,mBAAA,CAAuD,OAAvD,GAAA,EAAuD,EAAA,gBAAA,CAAzB,EAAA,UAAU,EAAG,QAAK,CAAA,IAAA,EAChD,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAIM,MAAA,CAJD,OAAA,EAAK,EAAA,eAAA,CAAA,CAAC,gBAAe,CAAA,UAAsB,EAAA,KAAM,CAAA,CAAA,EAAI,QAAQ,YAAY,KAAK,OACjF,MAAM,4BACN,EAAA,CAAA,GAAA,AAAA,EAAA,KAAA,EAAA,EAAA,EAAA,mBAAA,CAC4B,OAAA,CADtB,EAAE,uBAAuB,OAAO,eAAe,eAAa,MAAM,iBAAe,QACrF,kBAAgB,OAKtB,EAAA,KAAA,EAAA,CAAA,CAAA,EAAA,CAAA,EAAA,EAAA,CAAA,GAAA,EAAA,EAAA,YAAA,CAQa,EAAA,WAAA,CARD,KAAK,eAAe,EAAA,CAC9B,SAAA,EAAA,EAAA,QAAA,KAMK,CANK,EAAA,QAAA,EAAV,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAMK,KANL,EAMK,GAAA,EALH,EAAA,UAAA,CAAA,EAAA,GAAA,EAAA,EAAA,mBAAA,CAIK,EAAA,SAAA,MAAA,EAAA,EAAA,WAAA,CAJuB,EAAA,SAAhB,EAAM,MAAlB,EAAA,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAIK,KAAA,CAJiC,IAAK,EAAK,OAAS,EAAO,OAAA,EAAK,EAAA,eAAA,CAAA,CAAC,gBAAe,CAAA,cAC1D,EAAK,QAAQ,CAAA,CAAA,EAAK,QAAK,GAAE,EAAa,CAAI,CACvD,EAAA,CAAA,EAAK,OAAA,EAAjB,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAAmE,OAAA,OAA5C,MAAM,YAAY,UAAQ,EAAK,IACtD,EAAA,KAAA,EAAA,CAAA,IAAA,EAAA,EAAA,mBAAA,CAAA,GAAA,EAAA,GAAA,EAAA,EAAA,mBAAA,CAAgD,OAAhD,GAAA,EAAgD,EAAA,gBAAA,CAApB,EAAK,KAAK,EAAA,CAAA,CAAA,EAAA,GAAA,CAAA,iGCVhD,EAAe"}
|
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
|
+
}
|