cisse-vue-ui 0.1.7 → 0.1.8
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/dist/{CollapsibleCard.vue_vue_type_script_setup_true_lang-UHaD0-Ou.cjs → CollapsibleCard.vue_vue_type_script_setup_true_lang-Bd6TPEpH.cjs} +49 -219
- package/dist/CollapsibleCard.vue_vue_type_script_setup_true_lang-Bd6TPEpH.cjs.map +1 -0
- package/dist/{CollapsibleCard.vue_vue_type_script_setup_true_lang-gREq11EV.js → CollapsibleCard.vue_vue_type_script_setup_true_lang-l4KQ8HX8.js} +56 -226
- package/dist/CollapsibleCard.vue_vue_type_script_setup_true_lang-l4KQ8HX8.js.map +1 -0
- package/dist/Dropdown.vue_vue_type_script_setup_true_lang-CZQ5Ci8K.cjs +258 -0
- package/dist/Dropdown.vue_vue_type_script_setup_true_lang-CZQ5Ci8K.cjs.map +1 -0
- package/dist/Dropdown.vue_vue_type_script_setup_true_lang-CfyqlT_n.js +259 -0
- package/dist/Dropdown.vue_vue_type_script_setup_true_lang-CfyqlT_n.js.map +1 -0
- package/dist/{PageLayout.vue_vue_type_script_setup_true_lang-CQ7jdAeN.js → PageLayout.vue_vue_type_script_setup_true_lang-BoFiqN42.js} +78 -9
- package/dist/PageLayout.vue_vue_type_script_setup_true_lang-BoFiqN42.js.map +1 -0
- package/dist/{PageLayout.vue_vue_type_script_setup_true_lang-Cy0-c7YG.cjs → PageLayout.vue_vue_type_script_setup_true_lang-HxuODTiQ.cjs} +78 -9
- package/dist/PageLayout.vue_vue_type_script_setup_true_lang-HxuODTiQ.cjs.map +1 -0
- package/dist/components/core/Dropdown.vue.d.ts +6 -2
- package/dist/components/core/index.cjs +6 -6
- package/dist/components/core/index.js +5 -5
- package/dist/components/index.cjs +7 -7
- package/dist/components/index.js +13 -13
- package/dist/components/layout/BaseLayout.vue.d.ts +15 -0
- package/dist/components/layout/index.cjs +1 -1
- package/dist/components/layout/index.js +1 -1
- package/dist/{index-ufbk3IV7.js → index-3tIGVZX9.js} +4 -4
- package/dist/index-3tIGVZX9.js.map +1 -0
- package/dist/{index-HxEv4Ixf.cjs → index-CjRzry8D.cjs} +8 -8
- package/dist/index-CjRzry8D.cjs.map +1 -0
- package/dist/index.cjs +8 -8
- package/dist/index.js +16 -16
- package/dist/style.css +1 -1
- package/package.json +1 -1
- package/dist/CollapsibleCard.vue_vue_type_script_setup_true_lang-UHaD0-Ou.cjs.map +0 -1
- package/dist/CollapsibleCard.vue_vue_type_script_setup_true_lang-gREq11EV.js.map +0 -1
- package/dist/MenuItem.vue_vue_type_script_setup_true_lang-BWz86k7c.cjs +0 -86
- package/dist/MenuItem.vue_vue_type_script_setup_true_lang-BWz86k7c.cjs.map +0 -1
- package/dist/MenuItem.vue_vue_type_script_setup_true_lang-emN2qlfR.js +0 -87
- package/dist/MenuItem.vue_vue_type_script_setup_true_lang-emN2qlfR.js.map +0 -1
- package/dist/PageLayout.vue_vue_type_script_setup_true_lang-CQ7jdAeN.js.map +0 -1
- package/dist/PageLayout.vue_vue_type_script_setup_true_lang-Cy0-c7YG.cjs.map +0 -1
- package/dist/index-HxEv4Ixf.cjs.map +0 -1
- package/dist/index-ufbk3IV7.js.map +0 -1
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
const vue = require("vue");
|
|
3
|
+
const vue$1 = require("@iconify/vue");
|
|
4
|
+
const _hoisted_1$1 = { class: "relative" };
|
|
5
|
+
const _hoisted_2$1 = {
|
|
6
|
+
key: 0,
|
|
7
|
+
class: "absolute top-0.25 right-0.25 size-1.5 rounded-full bg-red-600"
|
|
8
|
+
};
|
|
9
|
+
const _sfc_main$1 = /* @__PURE__ */ vue.defineComponent({
|
|
10
|
+
__name: "MenuItem",
|
|
11
|
+
props: {
|
|
12
|
+
menuItem: {},
|
|
13
|
+
expanded: { type: Boolean, default: true },
|
|
14
|
+
active: { type: Boolean, default: void 0 },
|
|
15
|
+
currentPath: { default: void 0 }
|
|
16
|
+
},
|
|
17
|
+
setup(__props) {
|
|
18
|
+
const props = __props;
|
|
19
|
+
const isRouteActive = vue.computed(() => {
|
|
20
|
+
if (props.active !== void 0) {
|
|
21
|
+
return props.active;
|
|
22
|
+
}
|
|
23
|
+
const path = props.currentPath ?? (typeof window !== "undefined" ? window.location.pathname : "/");
|
|
24
|
+
if (props.menuItem.link === "/") {
|
|
25
|
+
return path === "/";
|
|
26
|
+
}
|
|
27
|
+
return path === props.menuItem.link || path.startsWith(props.menuItem.link + "/");
|
|
28
|
+
});
|
|
29
|
+
const linkComponent = vue.computed(() => {
|
|
30
|
+
try {
|
|
31
|
+
const RouterLink = vue.resolveComponent("RouterLink");
|
|
32
|
+
if (typeof RouterLink !== "string") {
|
|
33
|
+
return RouterLink;
|
|
34
|
+
}
|
|
35
|
+
} catch {
|
|
36
|
+
}
|
|
37
|
+
return "a";
|
|
38
|
+
});
|
|
39
|
+
const linkProps = vue.computed(() => {
|
|
40
|
+
if (linkComponent.value === "a") {
|
|
41
|
+
return { href: props.menuItem.link };
|
|
42
|
+
}
|
|
43
|
+
return { to: props.menuItem.link };
|
|
44
|
+
});
|
|
45
|
+
return (_ctx, _cache) => {
|
|
46
|
+
return vue.openBlock(), vue.createBlock(vue.resolveDynamicComponent(linkComponent.value), vue.mergeProps(linkProps.value, {
|
|
47
|
+
class: [__props.expanded ? "flex-row" : "flex-col", "group relative flex items-center justify-center gap-2 px-5"]
|
|
48
|
+
}), {
|
|
49
|
+
default: vue.withCtx(() => [
|
|
50
|
+
vue.createElementVNode("div", _hoisted_1$1, [
|
|
51
|
+
vue.createVNode(vue.unref(vue$1.Icon), {
|
|
52
|
+
class: vue.normalizeClass([[
|
|
53
|
+
isRouteActive.value ? "text-white" : "text-white/50 group-hover:text-white/80 dark:text-gray-700 dark:group-hover:text-gray-500",
|
|
54
|
+
__props.expanded ? "size-6" : "size-8"
|
|
55
|
+
], "transition-all duration-300"]),
|
|
56
|
+
icon: __props.menuItem.icon
|
|
57
|
+
}, null, 8, ["class", "icon"]),
|
|
58
|
+
__props.menuItem.notification ? (vue.openBlock(), vue.createElementBlock("span", _hoisted_2$1)) : vue.createCommentVNode("", true)
|
|
59
|
+
]),
|
|
60
|
+
vue.createVNode(vue.Transition, {
|
|
61
|
+
"enter-active-class": "transition-all duration-300 ease-out",
|
|
62
|
+
"enter-from-class": "opacity-0 -translate-x-2",
|
|
63
|
+
"enter-to-class": "opacity-100 translate-x-0",
|
|
64
|
+
"leave-active-class": "transition-all duration-200 ease-in",
|
|
65
|
+
"leave-from-class": "opacity-100 translate-x-0",
|
|
66
|
+
"leave-to-class": "opacity-0 -translate-x-2"
|
|
67
|
+
}, {
|
|
68
|
+
default: vue.withCtx(() => [
|
|
69
|
+
__props.expanded ? (vue.openBlock(), vue.createElementBlock("span", {
|
|
70
|
+
key: 0,
|
|
71
|
+
class: vue.normalizeClass([
|
|
72
|
+
isRouteActive.value ? "text-white" : "text-white/50 group-hover:text-white/80 dark:text-gray-700 dark:group-hover:text-gray-500",
|
|
73
|
+
"text-sm font-semibold whitespace-nowrap"
|
|
74
|
+
])
|
|
75
|
+
}, vue.toDisplayString(__props.menuItem.label), 3)) : vue.createCommentVNode("", true)
|
|
76
|
+
]),
|
|
77
|
+
_: 1
|
|
78
|
+
})
|
|
79
|
+
]),
|
|
80
|
+
_: 1
|
|
81
|
+
}, 16, ["class"]);
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
const _hoisted_1 = {
|
|
86
|
+
type: "button",
|
|
87
|
+
class: "inline-flex items-center gap-2 rounded-lg border border-gray-300 bg-white px-4 py-2 text-sm font-medium text-gray-700 hover:bg-gray-50 dark:border-gray-600 dark:bg-gray-800 dark:text-gray-200 dark:hover:bg-gray-700"
|
|
88
|
+
};
|
|
89
|
+
const _hoisted_2 = {
|
|
90
|
+
key: 0,
|
|
91
|
+
class: "my-1 border-t border-gray-200 dark:border-gray-700"
|
|
92
|
+
};
|
|
93
|
+
const _hoisted_3 = ["disabled", "onClick"];
|
|
94
|
+
const _sfc_main = /* @__PURE__ */ vue.defineComponent({
|
|
95
|
+
__name: "Dropdown",
|
|
96
|
+
props: {
|
|
97
|
+
items: { default: () => [] },
|
|
98
|
+
align: { default: "left" },
|
|
99
|
+
width: { default: "auto" },
|
|
100
|
+
teleport: { type: Boolean, default: true }
|
|
101
|
+
},
|
|
102
|
+
emits: ["select"],
|
|
103
|
+
setup(__props, { emit: __emit }) {
|
|
104
|
+
const props = __props;
|
|
105
|
+
const emit = __emit;
|
|
106
|
+
const isOpen = vue.ref(false);
|
|
107
|
+
const dropdownRef = vue.ref();
|
|
108
|
+
const triggerRef = vue.ref();
|
|
109
|
+
const menuRef = vue.ref();
|
|
110
|
+
const dropdownPosition = vue.ref({ top: 0, left: 0, width: 0 });
|
|
111
|
+
const updatePosition = () => {
|
|
112
|
+
if (!triggerRef.value || !props.teleport) return;
|
|
113
|
+
const rect = triggerRef.value.getBoundingClientRect();
|
|
114
|
+
dropdownPosition.value = {
|
|
115
|
+
top: rect.bottom + window.scrollY + 8,
|
|
116
|
+
left: props.align === "right" ? rect.right + window.scrollX : rect.left + window.scrollX,
|
|
117
|
+
width: rect.width
|
|
118
|
+
};
|
|
119
|
+
};
|
|
120
|
+
const toggle = () => {
|
|
121
|
+
isOpen.value = !isOpen.value;
|
|
122
|
+
if (isOpen.value) {
|
|
123
|
+
vue.nextTick(updatePosition);
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
const close = () => {
|
|
127
|
+
isOpen.value = false;
|
|
128
|
+
};
|
|
129
|
+
const selectItem = (item) => {
|
|
130
|
+
if (item.disabled || item.divider) return;
|
|
131
|
+
emit("select", item);
|
|
132
|
+
close();
|
|
133
|
+
};
|
|
134
|
+
const handleClickOutside = (event) => {
|
|
135
|
+
var _a, _b;
|
|
136
|
+
const target = event.target;
|
|
137
|
+
const isInsideTrigger = (_a = triggerRef.value) == null ? void 0 : _a.contains(target);
|
|
138
|
+
const isInsideMenu = (_b = menuRef.value) == null ? void 0 : _b.contains(target);
|
|
139
|
+
if (!isInsideTrigger && !isInsideMenu) {
|
|
140
|
+
close();
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
vue.watch(isOpen, (newValue) => {
|
|
144
|
+
if (newValue) {
|
|
145
|
+
document.addEventListener("click", handleClickOutside);
|
|
146
|
+
window.addEventListener("scroll", updatePosition, true);
|
|
147
|
+
window.addEventListener("resize", updatePosition);
|
|
148
|
+
} else {
|
|
149
|
+
document.removeEventListener("click", handleClickOutside);
|
|
150
|
+
window.removeEventListener("scroll", updatePosition, true);
|
|
151
|
+
window.removeEventListener("resize", updatePosition);
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
vue.onUnmounted(() => {
|
|
155
|
+
document.removeEventListener("click", handleClickOutside);
|
|
156
|
+
window.removeEventListener("scroll", updatePosition, true);
|
|
157
|
+
window.removeEventListener("resize", updatePosition);
|
|
158
|
+
});
|
|
159
|
+
const widthClasses = {
|
|
160
|
+
auto: "w-auto min-w-[10rem]",
|
|
161
|
+
full: "w-full",
|
|
162
|
+
sm: "w-32",
|
|
163
|
+
md: "w-48",
|
|
164
|
+
lg: "w-64"
|
|
165
|
+
};
|
|
166
|
+
const dropdownStyle = vue.computed(() => {
|
|
167
|
+
if (!props.teleport) return {};
|
|
168
|
+
return {
|
|
169
|
+
position: "absolute",
|
|
170
|
+
top: `${dropdownPosition.value.top}px`,
|
|
171
|
+
left: props.align === "right" ? "auto" : `${dropdownPosition.value.left}px`,
|
|
172
|
+
right: props.align === "right" ? `${window.innerWidth - dropdownPosition.value.left - dropdownPosition.value.width}px` : "auto"
|
|
173
|
+
};
|
|
174
|
+
});
|
|
175
|
+
return (_ctx, _cache) => {
|
|
176
|
+
return vue.openBlock(), vue.createElementBlock("div", {
|
|
177
|
+
ref_key: "dropdownRef",
|
|
178
|
+
ref: dropdownRef,
|
|
179
|
+
class: "relative inline-block"
|
|
180
|
+
}, [
|
|
181
|
+
vue.createElementVNode("div", {
|
|
182
|
+
ref_key: "triggerRef",
|
|
183
|
+
ref: triggerRef,
|
|
184
|
+
onClick: toggle
|
|
185
|
+
}, [
|
|
186
|
+
vue.renderSlot(_ctx.$slots, "trigger", {}, () => [
|
|
187
|
+
vue.createElementVNode("button", _hoisted_1, [
|
|
188
|
+
vue.renderSlot(_ctx.$slots, "trigger-label", {}, () => [
|
|
189
|
+
_cache[0] || (_cache[0] = vue.createTextVNode("Options", -1))
|
|
190
|
+
]),
|
|
191
|
+
vue.createVNode(vue.unref(vue$1.Icon), {
|
|
192
|
+
icon: "lucide:chevron-down",
|
|
193
|
+
class: vue.normalizeClass(["size-4 transition-transform", isOpen.value && "rotate-180"])
|
|
194
|
+
}, null, 8, ["class"])
|
|
195
|
+
])
|
|
196
|
+
])
|
|
197
|
+
], 512),
|
|
198
|
+
(vue.openBlock(), vue.createBlock(vue.Teleport, {
|
|
199
|
+
to: "body",
|
|
200
|
+
disabled: !__props.teleport
|
|
201
|
+
}, [
|
|
202
|
+
vue.createVNode(vue.Transition, {
|
|
203
|
+
"enter-active-class": "transition ease-out duration-100",
|
|
204
|
+
"enter-from-class": "transform opacity-0 scale-95",
|
|
205
|
+
"enter-to-class": "transform opacity-100 scale-100",
|
|
206
|
+
"leave-active-class": "transition ease-in duration-75",
|
|
207
|
+
"leave-from-class": "transform opacity-100 scale-100",
|
|
208
|
+
"leave-to-class": "transform opacity-0 scale-95"
|
|
209
|
+
}, {
|
|
210
|
+
default: vue.withCtx(() => [
|
|
211
|
+
isOpen.value ? (vue.openBlock(), vue.createElementBlock("div", {
|
|
212
|
+
key: 0,
|
|
213
|
+
ref_key: "menuRef",
|
|
214
|
+
ref: menuRef,
|
|
215
|
+
style: vue.normalizeStyle(dropdownStyle.value),
|
|
216
|
+
class: vue.normalizeClass([
|
|
217
|
+
"z-[9999] rounded-lg border border-gray-200 bg-white py-1 shadow-lg dark:border-gray-700 dark:bg-gray-800",
|
|
218
|
+
widthClasses[__props.width],
|
|
219
|
+
!__props.teleport && (__props.align === "right" ? "absolute mt-2 right-0" : "absolute mt-2 left-0")
|
|
220
|
+
])
|
|
221
|
+
}, [
|
|
222
|
+
vue.renderSlot(_ctx.$slots, "default", { close }, () => [
|
|
223
|
+
(vue.openBlock(true), vue.createElementBlock(vue.Fragment, null, vue.renderList(__props.items, (item) => {
|
|
224
|
+
return vue.openBlock(), vue.createElementBlock(vue.Fragment, {
|
|
225
|
+
key: item.key
|
|
226
|
+
}, [
|
|
227
|
+
item.divider ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_2)) : (vue.openBlock(), vue.createElementBlock("button", {
|
|
228
|
+
key: 1,
|
|
229
|
+
type: "button",
|
|
230
|
+
disabled: item.disabled,
|
|
231
|
+
class: vue.normalizeClass([
|
|
232
|
+
"flex w-full items-center gap-2 px-4 py-2 text-left text-sm transition-colors",
|
|
233
|
+
item.disabled ? "cursor-not-allowed opacity-50" : item.danger ? "text-red-600 hover:bg-red-50 dark:text-red-400 dark:hover:bg-red-900/20" : "text-gray-700 hover:bg-gray-100 dark:text-gray-200 dark:hover:bg-gray-700"
|
|
234
|
+
]),
|
|
235
|
+
onClick: ($event) => selectItem(item)
|
|
236
|
+
}, [
|
|
237
|
+
item.icon ? (vue.openBlock(), vue.createBlock(vue.unref(vue$1.Icon), {
|
|
238
|
+
key: 0,
|
|
239
|
+
icon: item.icon,
|
|
240
|
+
class: "size-4"
|
|
241
|
+
}, null, 8, ["icon"])) : vue.createCommentVNode("", true),
|
|
242
|
+
vue.createTextVNode(" " + vue.toDisplayString(item.label), 1)
|
|
243
|
+
], 10, _hoisted_3))
|
|
244
|
+
], 64);
|
|
245
|
+
}), 128))
|
|
246
|
+
])
|
|
247
|
+
], 6)) : vue.createCommentVNode("", true)
|
|
248
|
+
]),
|
|
249
|
+
_: 3
|
|
250
|
+
})
|
|
251
|
+
], 8, ["disabled"]))
|
|
252
|
+
], 512);
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
});
|
|
256
|
+
exports._sfc_main = _sfc_main$1;
|
|
257
|
+
exports._sfc_main$1 = _sfc_main;
|
|
258
|
+
//# sourceMappingURL=Dropdown.vue_vue_type_script_setup_true_lang-CZQ5Ci8K.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Dropdown.vue_vue_type_script_setup_true_lang-CZQ5Ci8K.cjs","sources":["../src/components/core/MenuItem.vue","../src/components/core/Dropdown.vue"],"sourcesContent":["<script lang=\"ts\" setup>\nimport { computed, resolveComponent } from 'vue'\nimport { Icon } from '@iconify/vue'\nimport type { MenuItemProps } from '@/types'\n\nconst props = withDefaults(\n defineProps<{\n menuItem: MenuItemProps\n /** Whether sidebar is expanded (shows labels) */\n expanded?: boolean\n /** Override active state directly */\n active?: boolean\n /** Current route path (pass from parent using useRoute().path) */\n currentPath?: string\n }>(),\n {\n expanded: true,\n active: undefined,\n currentPath: undefined,\n },\n)\n\nconst isRouteActive = computed(() => {\n // If active prop is explicitly set, use it\n if (props.active !== undefined) {\n return props.active\n }\n\n // Use currentPath prop if provided, otherwise fall back to window.location\n const path = props.currentPath ?? (typeof window !== 'undefined' ? window.location.pathname : '/')\n\n if (props.menuItem.link === '/') {\n return path === '/'\n }\n return path === props.menuItem.link || path.startsWith(props.menuItem.link + '/')\n})\n\n// Try to resolve RouterLink, fallback to 'a' tag\nconst linkComponent = computed(() => {\n try {\n const RouterLink = resolveComponent('RouterLink')\n if (typeof RouterLink !== 'string') {\n return RouterLink\n }\n } catch {\n // RouterLink not available\n }\n return 'a'\n})\n\nconst linkProps = computed(() => {\n if (linkComponent.value === 'a') {\n return { href: props.menuItem.link }\n }\n return { to: props.menuItem.link }\n})\n</script>\n\n<template>\n <component\n :is=\"linkComponent\"\n v-bind=\"linkProps\"\n :class=\"expanded ? 'flex-row' : 'flex-col'\"\n class=\"group relative flex items-center justify-center gap-2 px-5\"\n >\n <div class=\"relative\">\n <Icon\n :class=\"[\n isRouteActive\n ? 'text-white'\n : 'text-white/50 group-hover:text-white/80 dark:text-gray-700 dark:group-hover:text-gray-500',\n expanded ? 'size-6' : 'size-8',\n ]\"\n class=\"transition-all duration-300\"\n :icon=\"menuItem.icon\"\n />\n\n <span\n v-if=\"menuItem.notification\"\n class=\"absolute top-0.25 right-0.25 size-1.5 rounded-full bg-red-600\"\n ></span>\n </div>\n\n <Transition\n enter-active-class=\"transition-all duration-300 ease-out\"\n enter-from-class=\"opacity-0 -translate-x-2\"\n enter-to-class=\"opacity-100 translate-x-0\"\n leave-active-class=\"transition-all duration-200 ease-in\"\n leave-from-class=\"opacity-100 translate-x-0\"\n leave-to-class=\"opacity-0 -translate-x-2\"\n >\n <span\n v-if=\"expanded\"\n :class=\"\n isRouteActive\n ? 'text-white'\n : 'text-white/50 group-hover:text-white/80 dark:text-gray-700 dark:group-hover:text-gray-500'\n \"\n class=\"text-sm font-semibold whitespace-nowrap\"\n >{{ menuItem.label }}</span\n >\n </Transition>\n </component>\n</template>\n","<script lang=\"ts\" setup>\nimport { ref, computed, onUnmounted, nextTick, watch } from 'vue'\nimport { Icon } from '@iconify/vue'\n\nexport interface DropdownItem {\n key: string\n label: string\n icon?: string\n disabled?: boolean\n danger?: boolean\n divider?: boolean\n}\n\nconst props = withDefaults(\n defineProps<{\n /** Dropdown items (optional if using default slot) */\n items?: DropdownItem[]\n /** Align dropdown */\n align?: 'left' | 'right'\n /** Dropdown width */\n width?: 'auto' | 'full' | 'sm' | 'md' | 'lg'\n /** Use teleport to body to avoid overflow clipping */\n teleport?: boolean\n }>(),\n {\n items: () => [],\n align: 'left',\n width: 'auto',\n teleport: true,\n },\n)\n\nconst emit = defineEmits<{\n select: [item: DropdownItem]\n}>()\n\nconst isOpen = ref(false)\nconst dropdownRef = ref<HTMLElement>()\nconst triggerRef = ref<HTMLElement>()\nconst menuRef = ref<HTMLElement>()\nconst dropdownPosition = ref({ top: 0, left: 0, width: 0 })\n\nconst updatePosition = () => {\n if (!triggerRef.value || !props.teleport) return\n const rect = triggerRef.value.getBoundingClientRect()\n dropdownPosition.value = {\n top: rect.bottom + window.scrollY + 8,\n left: props.align === 'right' ? rect.right + window.scrollX : rect.left + window.scrollX,\n width: rect.width,\n }\n}\n\nconst toggle = () => {\n isOpen.value = !isOpen.value\n if (isOpen.value) {\n nextTick(updatePosition)\n }\n}\n\nconst close = () => {\n isOpen.value = false\n}\n\nconst selectItem = (item: DropdownItem) => {\n if (item.disabled || item.divider) return\n emit('select', item)\n close()\n}\n\nconst handleClickOutside = (event: MouseEvent) => {\n const target = event.target as Node\n const isInsideTrigger = triggerRef.value?.contains(target)\n const isInsideMenu = menuRef.value?.contains(target)\n if (!isInsideTrigger && !isInsideMenu) {\n close()\n }\n}\n\nwatch(isOpen, (newValue) => {\n if (newValue) {\n document.addEventListener('click', handleClickOutside)\n window.addEventListener('scroll', updatePosition, true)\n window.addEventListener('resize', updatePosition)\n } else {\n document.removeEventListener('click', handleClickOutside)\n window.removeEventListener('scroll', updatePosition, true)\n window.removeEventListener('resize', updatePosition)\n }\n})\n\nonUnmounted(() => {\n document.removeEventListener('click', handleClickOutside)\n window.removeEventListener('scroll', updatePosition, true)\n window.removeEventListener('resize', updatePosition)\n})\n\nconst widthClasses = {\n auto: 'w-auto min-w-[10rem]',\n full: 'w-full',\n sm: 'w-32',\n md: 'w-48',\n lg: 'w-64',\n}\n\nconst dropdownStyle = computed(() => {\n if (!props.teleport) return {}\n return {\n position: 'absolute' as const,\n top: `${dropdownPosition.value.top}px`,\n left: props.align === 'right' ? 'auto' : `${dropdownPosition.value.left}px`,\n right: props.align === 'right' ? `${window.innerWidth - dropdownPosition.value.left - dropdownPosition.value.width}px` : 'auto',\n }\n})\n</script>\n\n<template>\n <div ref=\"dropdownRef\" class=\"relative inline-block\">\n <div ref=\"triggerRef\" @click=\"toggle\">\n <slot name=\"trigger\">\n <button\n type=\"button\"\n class=\"inline-flex items-center gap-2 rounded-lg border border-gray-300 bg-white px-4 py-2 text-sm font-medium text-gray-700 hover:bg-gray-50 dark:border-gray-600 dark:bg-gray-800 dark:text-gray-200 dark:hover:bg-gray-700\"\n >\n <slot name=\"trigger-label\">Options</slot>\n <Icon\n icon=\"lucide:chevron-down\"\n :class=\"['size-4 transition-transform', isOpen && 'rotate-180']\"\n />\n </button>\n </slot>\n </div>\n\n <Teleport to=\"body\" :disabled=\"!teleport\">\n <Transition\n enter-active-class=\"transition ease-out duration-100\"\n enter-from-class=\"transform opacity-0 scale-95\"\n enter-to-class=\"transform opacity-100 scale-100\"\n leave-active-class=\"transition ease-in duration-75\"\n leave-from-class=\"transform opacity-100 scale-100\"\n leave-to-class=\"transform opacity-0 scale-95\"\n >\n <div\n v-if=\"isOpen\"\n ref=\"menuRef\"\n :style=\"dropdownStyle\"\n :class=\"[\n 'z-[9999] rounded-lg border border-gray-200 bg-white py-1 shadow-lg dark:border-gray-700 dark:bg-gray-800',\n widthClasses[width],\n !teleport && (align === 'right' ? 'absolute mt-2 right-0' : 'absolute mt-2 left-0'),\n ]\"\n >\n <!-- Custom content via default slot -->\n <slot :close=\"close\">\n <!-- Default items rendering -->\n <template v-for=\"item in items\" :key=\"item.key\">\n <div\n v-if=\"item.divider\"\n class=\"my-1 border-t border-gray-200 dark:border-gray-700\"\n />\n <button\n v-else\n type=\"button\"\n :disabled=\"item.disabled\"\n :class=\"[\n 'flex w-full items-center gap-2 px-4 py-2 text-left text-sm transition-colors',\n item.disabled\n ? 'cursor-not-allowed opacity-50'\n : item.danger\n ? 'text-red-600 hover:bg-red-50 dark:text-red-400 dark:hover:bg-red-900/20'\n : 'text-gray-700 hover:bg-gray-100 dark:text-gray-200 dark:hover:bg-gray-700',\n ]\"\n @click=\"selectItem(item)\"\n >\n <Icon v-if=\"item.icon\" :icon=\"item.icon\" class=\"size-4\" />\n {{ item.label }}\n </button>\n </template>\n </slot>\n </div>\n </Transition>\n </Teleport>\n </div>\n</template>\n"],"names":["computed","resolveComponent","_openBlock","_createBlock","_resolveDynamicComponent","_mergeProps","_createElementVNode","_hoisted_1","_createVNode","_unref","Icon","_normalizeClass","_createElementBlock","_hoisted_2","_Transition","_toDisplayString","ref","nextTick","watch","onUnmounted","_renderSlot","_Teleport","_Fragment","_renderList"],"mappings":";;;;;;;;;;;;;;;;;AAKA,UAAM,QAAQ;AAiBd,UAAM,gBAAgBA,IAAAA,SAAS,MAAM;AAEnC,UAAI,MAAM,WAAW,QAAW;AAC9B,eAAO,MAAM;AAAA,MACf;AAGA,YAAM,OAAO,MAAM,gBAAgB,OAAO,WAAW,cAAc,OAAO,SAAS,WAAW;AAE9F,UAAI,MAAM,SAAS,SAAS,KAAK;AAC/B,eAAO,SAAS;AAAA,MAClB;AACA,aAAO,SAAS,MAAM,SAAS,QAAQ,KAAK,WAAW,MAAM,SAAS,OAAO,GAAG;AAAA,IAClF,CAAC;AAGD,UAAM,gBAAgBA,IAAAA,SAAS,MAAM;AACnC,UAAI;AACF,cAAM,aAAaC,IAAAA,iBAAiB,YAAY;AAChD,YAAI,OAAO,eAAe,UAAU;AAClC,iBAAO;AAAA,QACT;AAAA,MACF,QAAQ;AAAA,MAER;AACA,aAAO;AAAA,IACT,CAAC;AAED,UAAM,YAAYD,IAAAA,SAAS,MAAM;AAC/B,UAAI,cAAc,UAAU,KAAK;AAC/B,eAAO,EAAE,MAAM,MAAM,SAAS,KAAA;AAAA,MAChC;AACA,aAAO,EAAE,IAAI,MAAM,SAAS,KAAA;AAAA,IAC9B,CAAC;;AAIC,aAAAE,IAAAA,UAAA,GAAAC,IAAAA,YA2CYC,4BA1CL,cAAA,KAAa,GADpBC,IAAAA,WAEU,UAyCE,OAzCO;AAAA,QAChB,OAAK,CAAE,QAAA,WAAQ,aAAA,YACV,4DAA4D;AAAA,MAAA;6BAElE,MAgBM;AAAA,UAhBNC,IAAAA,mBAgBM,OAhBNC,cAgBM;AAAA,YAfJC,gBASEC,IAAAA,MAAAC,MAAAA,IAAA,GAAA;AAAA,cARC,OAAKC,IAAAA,eAAA,CAAA;AAAA,gBAAc,cAAA;gBAA8J,QAAA,WAAQ,WAAA;AAAA,cAAA,GAMpL,6BAA6B,CAAA;AAAA,cAClC,MAAM,QAAA,SAAS;AAAA,YAAA;YAIV,QAAA,SAAS,gBADjBT,IAAAA,aAAAU,IAAAA,mBAGQ,QAHRC,YAGQ;;UAGVL,IAAAA,YAkBaM,IAAAA,YAAA;AAAA,YAjBX,sBAAmB;AAAA,YACnB,oBAAiB;AAAA,YACjB,kBAAe;AAAA,YACf,sBAAmB;AAAA,YACnB,oBAAiB;AAAA,YACjB,kBAAe;AAAA,UAAA;iCAEf,MASC;AAAA,cARO,QAAA,6BADRF,IAAAA,mBASC,QAAA;AAAA;gBAPE,OAAKD,IAAAA,eAAA;AAAA,kBAAa,cAAA;kBAKb;AAAA,gBAAA,CAAyC;AAAA,cAAA,GAC3CI,IAAAA,gBAAA,QAAA,SAAS,KAAK,GAAA,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACtF1B,UAAM,QAAQ;AAmBd,UAAM,OAAO;AAIb,UAAM,SAASC,IAAAA,IAAI,KAAK;AACxB,UAAM,cAAcA,IAAAA,IAAA;AACpB,UAAM,aAAaA,IAAAA,IAAA;AACnB,UAAM,UAAUA,IAAAA,IAAA;AAChB,UAAM,mBAAmBA,QAAI,EAAE,KAAK,GAAG,MAAM,GAAG,OAAO,GAAG;AAE1D,UAAM,iBAAiB,MAAM;AAC3B,UAAI,CAAC,WAAW,SAAS,CAAC,MAAM,SAAU;AAC1C,YAAM,OAAO,WAAW,MAAM,sBAAA;AAC9B,uBAAiB,QAAQ;AAAA,QACvB,KAAK,KAAK,SAAS,OAAO,UAAU;AAAA,QACpC,MAAM,MAAM,UAAU,UAAU,KAAK,QAAQ,OAAO,UAAU,KAAK,OAAO,OAAO;AAAA,QACjF,OAAO,KAAK;AAAA,MAAA;AAAA,IAEhB;AAEA,UAAM,SAAS,MAAM;AACnB,aAAO,QAAQ,CAAC,OAAO;AACvB,UAAI,OAAO,OAAO;AAChBC,YAAAA,SAAS,cAAc;AAAA,MACzB;AAAA,IACF;AAEA,UAAM,QAAQ,MAAM;AAClB,aAAO,QAAQ;AAAA,IACjB;AAEA,UAAM,aAAa,CAAC,SAAuB;AACzC,UAAI,KAAK,YAAY,KAAK,QAAS;AACnC,WAAK,UAAU,IAAI;AACnB,YAAA;AAAA,IACF;AAEA,UAAM,qBAAqB,CAAC,UAAsB;;AAChD,YAAM,SAAS,MAAM;AACrB,YAAM,mBAAkB,gBAAW,UAAX,mBAAkB,SAAS;AACnD,YAAM,gBAAe,aAAQ,UAAR,mBAAe,SAAS;AAC7C,UAAI,CAAC,mBAAmB,CAAC,cAAc;AACrC,cAAA;AAAA,MACF;AAAA,IACF;AAEAC,cAAM,QAAQ,CAAC,aAAa;AAC1B,UAAI,UAAU;AACZ,iBAAS,iBAAiB,SAAS,kBAAkB;AACrD,eAAO,iBAAiB,UAAU,gBAAgB,IAAI;AACtD,eAAO,iBAAiB,UAAU,cAAc;AAAA,MAClD,OAAO;AACL,iBAAS,oBAAoB,SAAS,kBAAkB;AACxD,eAAO,oBAAoB,UAAU,gBAAgB,IAAI;AACzD,eAAO,oBAAoB,UAAU,cAAc;AAAA,MACrD;AAAA,IACF,CAAC;AAEDC,QAAAA,YAAY,MAAM;AAChB,eAAS,oBAAoB,SAAS,kBAAkB;AACxD,aAAO,oBAAoB,UAAU,gBAAgB,IAAI;AACzD,aAAO,oBAAoB,UAAU,cAAc;AAAA,IACrD,CAAC;AAED,UAAM,eAAe;AAAA,MACnB,MAAM;AAAA,MACN,MAAM;AAAA,MACN,IAAI;AAAA,MACJ,IAAI;AAAA,MACJ,IAAI;AAAA,IAAA;AAGN,UAAM,gBAAgBnB,IAAAA,SAAS,MAAM;AACnC,UAAI,CAAC,MAAM,SAAU,QAAO,CAAA;AAC5B,aAAO;AAAA,QACL,UAAU;AAAA,QACV,KAAK,GAAG,iBAAiB,MAAM,GAAG;AAAA,QAClC,MAAM,MAAM,UAAU,UAAU,SAAS,GAAG,iBAAiB,MAAM,IAAI;AAAA,QACvE,OAAO,MAAM,UAAU,UAAU,GAAG,OAAO,aAAa,iBAAiB,MAAM,OAAO,iBAAiB,MAAM,KAAK,OAAO;AAAA,MAAA;AAAA,IAE7H,CAAC;;8BAICY,IAAAA,mBAiEM,OAAA;AAAA,iBAjEG;AAAA,QAAJ,KAAI;AAAA,QAAc,OAAM;AAAA,MAAA;QAC3BN,IAAAA,mBAaM,OAAA;AAAA,mBAbG;AAAA,UAAJ,KAAI;AAAA,UAAc,SAAO;AAAA,QAAA;UAC5Bc,IAAAA,WAWO,4BAXP,MAWO;AAAA,YAVLd,IAAAA,mBASS,UATT,YASS;AAAA,cALPc,IAAAA,WAAyC,kCAAzC,MAAyC;AAAA,8DAAd,WAAO,EAAA;AAAA,cAAA;cAClCZ,gBAGEC,IAAAA,MAAAC,MAAAA,IAAA,GAAA;AAAA,gBAFA,MAAK;AAAA,gBACJ,0DAAuC,OAAA,SAAM,YAAA,CAAA;AAAA,cAAA;;;;0BAMtDP,IAAAA,YAgDWkB,cAAA;AAAA,UAhDD,IAAG;AAAA,UAAQ,WAAW,QAAA;AAAA,QAAA;UAC9Bb,IAAAA,YA8CaM,IAAAA,YAAA;AAAA,YA7CX,sBAAmB;AAAA,YACnB,oBAAiB;AAAA,YACjB,kBAAe;AAAA,YACf,sBAAmB;AAAA,YACnB,oBAAiB;AAAA,YACjB,kBAAe;AAAA,UAAA;iCAEf,MAqCM;AAAA,cApCE,OAAA,0BADRF,IAAAA,mBAqCM,OAAA;AAAA;yBAnCA;AAAA,gBAAJ,KAAI;AAAA,gBACH,0BAAO,cAAA,KAAa;AAAA,gBACpB,OAAKD,IAAAA,eAAA;AAAA;kBAAwI,aAAa,QAAA,KAAK;AAAA,kBAAgB,CAAA,QAAA,aAAa,QAAA,UAAK,UAAA,0BAAA;AAAA,gBAAA;;gBAOlMS,IAAAA,WAyBO,KAAA,QAAA,WAAA,EAzBA,MAAA,GAAP,MAyBO;AAAA,wCAvBLR,IAAAA,mBAsBWU,IAAAA,UAAA,MAAAC,IAAAA,WAtBc,QAAA,OAAK,CAAb,SAAI;;sBAAiB,KAAA,KAAK;AAAA,oBAAA;sBAEjC,KAAK,WADbrB,cAAA,GAAAU,uBAGE,OAHF,UAGE,uBACFA,IAAAA,mBAgBS,UAAA;AAAA;wBAdP,MAAK;AAAA,wBACJ,UAAU,KAAK;AAAA,wBACf,OAAKD,IAAAA,eAAA;AAAA;0BAAwH,KAAK,6CAAqF,KAAK;;wBAQ5N,SAAK,CAAA,WAAE,WAAW,IAAI;AAAA,sBAAA;wBAEX,KAAK,yBAAjBR,IAAAA,YAA0DM,IAAAA,MAAAC,MAAAA,IAAA,GAAA;AAAA;0BAAlC,MAAM,KAAK;AAAA,0BAAM,OAAM;AAAA,wBAAA;4CAAW,MAC1DK,IAAAA,gBAAG,KAAK,KAAK,GAAA,CAAA;AAAA,sBAAA;;;;;;;;;;;;;;;"}
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
import { defineComponent, computed, resolveComponent, createBlock, openBlock, resolveDynamicComponent, mergeProps, withCtx, createElementVNode, createVNode, createElementBlock, createCommentVNode, unref, normalizeClass, Transition, toDisplayString, ref, watch, onUnmounted, renderSlot, createTextVNode, Teleport, normalizeStyle, Fragment, renderList, nextTick } from "vue";
|
|
2
|
+
import { Icon } from "@iconify/vue";
|
|
3
|
+
const _hoisted_1$1 = { class: "relative" };
|
|
4
|
+
const _hoisted_2$1 = {
|
|
5
|
+
key: 0,
|
|
6
|
+
class: "absolute top-0.25 right-0.25 size-1.5 rounded-full bg-red-600"
|
|
7
|
+
};
|
|
8
|
+
const _sfc_main$1 = /* @__PURE__ */ defineComponent({
|
|
9
|
+
__name: "MenuItem",
|
|
10
|
+
props: {
|
|
11
|
+
menuItem: {},
|
|
12
|
+
expanded: { type: Boolean, default: true },
|
|
13
|
+
active: { type: Boolean, default: void 0 },
|
|
14
|
+
currentPath: { default: void 0 }
|
|
15
|
+
},
|
|
16
|
+
setup(__props) {
|
|
17
|
+
const props = __props;
|
|
18
|
+
const isRouteActive = computed(() => {
|
|
19
|
+
if (props.active !== void 0) {
|
|
20
|
+
return props.active;
|
|
21
|
+
}
|
|
22
|
+
const path = props.currentPath ?? (typeof window !== "undefined" ? window.location.pathname : "/");
|
|
23
|
+
if (props.menuItem.link === "/") {
|
|
24
|
+
return path === "/";
|
|
25
|
+
}
|
|
26
|
+
return path === props.menuItem.link || path.startsWith(props.menuItem.link + "/");
|
|
27
|
+
});
|
|
28
|
+
const linkComponent = computed(() => {
|
|
29
|
+
try {
|
|
30
|
+
const RouterLink = resolveComponent("RouterLink");
|
|
31
|
+
if (typeof RouterLink !== "string") {
|
|
32
|
+
return RouterLink;
|
|
33
|
+
}
|
|
34
|
+
} catch {
|
|
35
|
+
}
|
|
36
|
+
return "a";
|
|
37
|
+
});
|
|
38
|
+
const linkProps = computed(() => {
|
|
39
|
+
if (linkComponent.value === "a") {
|
|
40
|
+
return { href: props.menuItem.link };
|
|
41
|
+
}
|
|
42
|
+
return { to: props.menuItem.link };
|
|
43
|
+
});
|
|
44
|
+
return (_ctx, _cache) => {
|
|
45
|
+
return openBlock(), createBlock(resolveDynamicComponent(linkComponent.value), mergeProps(linkProps.value, {
|
|
46
|
+
class: [__props.expanded ? "flex-row" : "flex-col", "group relative flex items-center justify-center gap-2 px-5"]
|
|
47
|
+
}), {
|
|
48
|
+
default: withCtx(() => [
|
|
49
|
+
createElementVNode("div", _hoisted_1$1, [
|
|
50
|
+
createVNode(unref(Icon), {
|
|
51
|
+
class: normalizeClass([[
|
|
52
|
+
isRouteActive.value ? "text-white" : "text-white/50 group-hover:text-white/80 dark:text-gray-700 dark:group-hover:text-gray-500",
|
|
53
|
+
__props.expanded ? "size-6" : "size-8"
|
|
54
|
+
], "transition-all duration-300"]),
|
|
55
|
+
icon: __props.menuItem.icon
|
|
56
|
+
}, null, 8, ["class", "icon"]),
|
|
57
|
+
__props.menuItem.notification ? (openBlock(), createElementBlock("span", _hoisted_2$1)) : createCommentVNode("", true)
|
|
58
|
+
]),
|
|
59
|
+
createVNode(Transition, {
|
|
60
|
+
"enter-active-class": "transition-all duration-300 ease-out",
|
|
61
|
+
"enter-from-class": "opacity-0 -translate-x-2",
|
|
62
|
+
"enter-to-class": "opacity-100 translate-x-0",
|
|
63
|
+
"leave-active-class": "transition-all duration-200 ease-in",
|
|
64
|
+
"leave-from-class": "opacity-100 translate-x-0",
|
|
65
|
+
"leave-to-class": "opacity-0 -translate-x-2"
|
|
66
|
+
}, {
|
|
67
|
+
default: withCtx(() => [
|
|
68
|
+
__props.expanded ? (openBlock(), createElementBlock("span", {
|
|
69
|
+
key: 0,
|
|
70
|
+
class: normalizeClass([
|
|
71
|
+
isRouteActive.value ? "text-white" : "text-white/50 group-hover:text-white/80 dark:text-gray-700 dark:group-hover:text-gray-500",
|
|
72
|
+
"text-sm font-semibold whitespace-nowrap"
|
|
73
|
+
])
|
|
74
|
+
}, toDisplayString(__props.menuItem.label), 3)) : createCommentVNode("", true)
|
|
75
|
+
]),
|
|
76
|
+
_: 1
|
|
77
|
+
})
|
|
78
|
+
]),
|
|
79
|
+
_: 1
|
|
80
|
+
}, 16, ["class"]);
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
const _hoisted_1 = {
|
|
85
|
+
type: "button",
|
|
86
|
+
class: "inline-flex items-center gap-2 rounded-lg border border-gray-300 bg-white px-4 py-2 text-sm font-medium text-gray-700 hover:bg-gray-50 dark:border-gray-600 dark:bg-gray-800 dark:text-gray-200 dark:hover:bg-gray-700"
|
|
87
|
+
};
|
|
88
|
+
const _hoisted_2 = {
|
|
89
|
+
key: 0,
|
|
90
|
+
class: "my-1 border-t border-gray-200 dark:border-gray-700"
|
|
91
|
+
};
|
|
92
|
+
const _hoisted_3 = ["disabled", "onClick"];
|
|
93
|
+
const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
94
|
+
__name: "Dropdown",
|
|
95
|
+
props: {
|
|
96
|
+
items: { default: () => [] },
|
|
97
|
+
align: { default: "left" },
|
|
98
|
+
width: { default: "auto" },
|
|
99
|
+
teleport: { type: Boolean, default: true }
|
|
100
|
+
},
|
|
101
|
+
emits: ["select"],
|
|
102
|
+
setup(__props, { emit: __emit }) {
|
|
103
|
+
const props = __props;
|
|
104
|
+
const emit = __emit;
|
|
105
|
+
const isOpen = ref(false);
|
|
106
|
+
const dropdownRef = ref();
|
|
107
|
+
const triggerRef = ref();
|
|
108
|
+
const menuRef = ref();
|
|
109
|
+
const dropdownPosition = ref({ top: 0, left: 0, width: 0 });
|
|
110
|
+
const updatePosition = () => {
|
|
111
|
+
if (!triggerRef.value || !props.teleport) return;
|
|
112
|
+
const rect = triggerRef.value.getBoundingClientRect();
|
|
113
|
+
dropdownPosition.value = {
|
|
114
|
+
top: rect.bottom + window.scrollY + 8,
|
|
115
|
+
left: props.align === "right" ? rect.right + window.scrollX : rect.left + window.scrollX,
|
|
116
|
+
width: rect.width
|
|
117
|
+
};
|
|
118
|
+
};
|
|
119
|
+
const toggle = () => {
|
|
120
|
+
isOpen.value = !isOpen.value;
|
|
121
|
+
if (isOpen.value) {
|
|
122
|
+
nextTick(updatePosition);
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
const close = () => {
|
|
126
|
+
isOpen.value = false;
|
|
127
|
+
};
|
|
128
|
+
const selectItem = (item) => {
|
|
129
|
+
if (item.disabled || item.divider) return;
|
|
130
|
+
emit("select", item);
|
|
131
|
+
close();
|
|
132
|
+
};
|
|
133
|
+
const handleClickOutside = (event) => {
|
|
134
|
+
var _a, _b;
|
|
135
|
+
const target = event.target;
|
|
136
|
+
const isInsideTrigger = (_a = triggerRef.value) == null ? void 0 : _a.contains(target);
|
|
137
|
+
const isInsideMenu = (_b = menuRef.value) == null ? void 0 : _b.contains(target);
|
|
138
|
+
if (!isInsideTrigger && !isInsideMenu) {
|
|
139
|
+
close();
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
watch(isOpen, (newValue) => {
|
|
143
|
+
if (newValue) {
|
|
144
|
+
document.addEventListener("click", handleClickOutside);
|
|
145
|
+
window.addEventListener("scroll", updatePosition, true);
|
|
146
|
+
window.addEventListener("resize", updatePosition);
|
|
147
|
+
} else {
|
|
148
|
+
document.removeEventListener("click", handleClickOutside);
|
|
149
|
+
window.removeEventListener("scroll", updatePosition, true);
|
|
150
|
+
window.removeEventListener("resize", updatePosition);
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
onUnmounted(() => {
|
|
154
|
+
document.removeEventListener("click", handleClickOutside);
|
|
155
|
+
window.removeEventListener("scroll", updatePosition, true);
|
|
156
|
+
window.removeEventListener("resize", updatePosition);
|
|
157
|
+
});
|
|
158
|
+
const widthClasses = {
|
|
159
|
+
auto: "w-auto min-w-[10rem]",
|
|
160
|
+
full: "w-full",
|
|
161
|
+
sm: "w-32",
|
|
162
|
+
md: "w-48",
|
|
163
|
+
lg: "w-64"
|
|
164
|
+
};
|
|
165
|
+
const dropdownStyle = computed(() => {
|
|
166
|
+
if (!props.teleport) return {};
|
|
167
|
+
return {
|
|
168
|
+
position: "absolute",
|
|
169
|
+
top: `${dropdownPosition.value.top}px`,
|
|
170
|
+
left: props.align === "right" ? "auto" : `${dropdownPosition.value.left}px`,
|
|
171
|
+
right: props.align === "right" ? `${window.innerWidth - dropdownPosition.value.left - dropdownPosition.value.width}px` : "auto"
|
|
172
|
+
};
|
|
173
|
+
});
|
|
174
|
+
return (_ctx, _cache) => {
|
|
175
|
+
return openBlock(), createElementBlock("div", {
|
|
176
|
+
ref_key: "dropdownRef",
|
|
177
|
+
ref: dropdownRef,
|
|
178
|
+
class: "relative inline-block"
|
|
179
|
+
}, [
|
|
180
|
+
createElementVNode("div", {
|
|
181
|
+
ref_key: "triggerRef",
|
|
182
|
+
ref: triggerRef,
|
|
183
|
+
onClick: toggle
|
|
184
|
+
}, [
|
|
185
|
+
renderSlot(_ctx.$slots, "trigger", {}, () => [
|
|
186
|
+
createElementVNode("button", _hoisted_1, [
|
|
187
|
+
renderSlot(_ctx.$slots, "trigger-label", {}, () => [
|
|
188
|
+
_cache[0] || (_cache[0] = createTextVNode("Options", -1))
|
|
189
|
+
]),
|
|
190
|
+
createVNode(unref(Icon), {
|
|
191
|
+
icon: "lucide:chevron-down",
|
|
192
|
+
class: normalizeClass(["size-4 transition-transform", isOpen.value && "rotate-180"])
|
|
193
|
+
}, null, 8, ["class"])
|
|
194
|
+
])
|
|
195
|
+
])
|
|
196
|
+
], 512),
|
|
197
|
+
(openBlock(), createBlock(Teleport, {
|
|
198
|
+
to: "body",
|
|
199
|
+
disabled: !__props.teleport
|
|
200
|
+
}, [
|
|
201
|
+
createVNode(Transition, {
|
|
202
|
+
"enter-active-class": "transition ease-out duration-100",
|
|
203
|
+
"enter-from-class": "transform opacity-0 scale-95",
|
|
204
|
+
"enter-to-class": "transform opacity-100 scale-100",
|
|
205
|
+
"leave-active-class": "transition ease-in duration-75",
|
|
206
|
+
"leave-from-class": "transform opacity-100 scale-100",
|
|
207
|
+
"leave-to-class": "transform opacity-0 scale-95"
|
|
208
|
+
}, {
|
|
209
|
+
default: withCtx(() => [
|
|
210
|
+
isOpen.value ? (openBlock(), createElementBlock("div", {
|
|
211
|
+
key: 0,
|
|
212
|
+
ref_key: "menuRef",
|
|
213
|
+
ref: menuRef,
|
|
214
|
+
style: normalizeStyle(dropdownStyle.value),
|
|
215
|
+
class: normalizeClass([
|
|
216
|
+
"z-[9999] rounded-lg border border-gray-200 bg-white py-1 shadow-lg dark:border-gray-700 dark:bg-gray-800",
|
|
217
|
+
widthClasses[__props.width],
|
|
218
|
+
!__props.teleport && (__props.align === "right" ? "absolute mt-2 right-0" : "absolute mt-2 left-0")
|
|
219
|
+
])
|
|
220
|
+
}, [
|
|
221
|
+
renderSlot(_ctx.$slots, "default", { close }, () => [
|
|
222
|
+
(openBlock(true), createElementBlock(Fragment, null, renderList(__props.items, (item) => {
|
|
223
|
+
return openBlock(), createElementBlock(Fragment, {
|
|
224
|
+
key: item.key
|
|
225
|
+
}, [
|
|
226
|
+
item.divider ? (openBlock(), createElementBlock("div", _hoisted_2)) : (openBlock(), createElementBlock("button", {
|
|
227
|
+
key: 1,
|
|
228
|
+
type: "button",
|
|
229
|
+
disabled: item.disabled,
|
|
230
|
+
class: normalizeClass([
|
|
231
|
+
"flex w-full items-center gap-2 px-4 py-2 text-left text-sm transition-colors",
|
|
232
|
+
item.disabled ? "cursor-not-allowed opacity-50" : item.danger ? "text-red-600 hover:bg-red-50 dark:text-red-400 dark:hover:bg-red-900/20" : "text-gray-700 hover:bg-gray-100 dark:text-gray-200 dark:hover:bg-gray-700"
|
|
233
|
+
]),
|
|
234
|
+
onClick: ($event) => selectItem(item)
|
|
235
|
+
}, [
|
|
236
|
+
item.icon ? (openBlock(), createBlock(unref(Icon), {
|
|
237
|
+
key: 0,
|
|
238
|
+
icon: item.icon,
|
|
239
|
+
class: "size-4"
|
|
240
|
+
}, null, 8, ["icon"])) : createCommentVNode("", true),
|
|
241
|
+
createTextVNode(" " + toDisplayString(item.label), 1)
|
|
242
|
+
], 10, _hoisted_3))
|
|
243
|
+
], 64);
|
|
244
|
+
}), 128))
|
|
245
|
+
])
|
|
246
|
+
], 6)) : createCommentVNode("", true)
|
|
247
|
+
]),
|
|
248
|
+
_: 3
|
|
249
|
+
})
|
|
250
|
+
], 8, ["disabled"]))
|
|
251
|
+
], 512);
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
});
|
|
255
|
+
export {
|
|
256
|
+
_sfc_main$1 as _,
|
|
257
|
+
_sfc_main as a
|
|
258
|
+
};
|
|
259
|
+
//# sourceMappingURL=Dropdown.vue_vue_type_script_setup_true_lang-CfyqlT_n.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Dropdown.vue_vue_type_script_setup_true_lang-CfyqlT_n.js","sources":["../src/components/core/MenuItem.vue","../src/components/core/Dropdown.vue"],"sourcesContent":["<script lang=\"ts\" setup>\nimport { computed, resolveComponent } from 'vue'\nimport { Icon } from '@iconify/vue'\nimport type { MenuItemProps } from '@/types'\n\nconst props = withDefaults(\n defineProps<{\n menuItem: MenuItemProps\n /** Whether sidebar is expanded (shows labels) */\n expanded?: boolean\n /** Override active state directly */\n active?: boolean\n /** Current route path (pass from parent using useRoute().path) */\n currentPath?: string\n }>(),\n {\n expanded: true,\n active: undefined,\n currentPath: undefined,\n },\n)\n\nconst isRouteActive = computed(() => {\n // If active prop is explicitly set, use it\n if (props.active !== undefined) {\n return props.active\n }\n\n // Use currentPath prop if provided, otherwise fall back to window.location\n const path = props.currentPath ?? (typeof window !== 'undefined' ? window.location.pathname : '/')\n\n if (props.menuItem.link === '/') {\n return path === '/'\n }\n return path === props.menuItem.link || path.startsWith(props.menuItem.link + '/')\n})\n\n// Try to resolve RouterLink, fallback to 'a' tag\nconst linkComponent = computed(() => {\n try {\n const RouterLink = resolveComponent('RouterLink')\n if (typeof RouterLink !== 'string') {\n return RouterLink\n }\n } catch {\n // RouterLink not available\n }\n return 'a'\n})\n\nconst linkProps = computed(() => {\n if (linkComponent.value === 'a') {\n return { href: props.menuItem.link }\n }\n return { to: props.menuItem.link }\n})\n</script>\n\n<template>\n <component\n :is=\"linkComponent\"\n v-bind=\"linkProps\"\n :class=\"expanded ? 'flex-row' : 'flex-col'\"\n class=\"group relative flex items-center justify-center gap-2 px-5\"\n >\n <div class=\"relative\">\n <Icon\n :class=\"[\n isRouteActive\n ? 'text-white'\n : 'text-white/50 group-hover:text-white/80 dark:text-gray-700 dark:group-hover:text-gray-500',\n expanded ? 'size-6' : 'size-8',\n ]\"\n class=\"transition-all duration-300\"\n :icon=\"menuItem.icon\"\n />\n\n <span\n v-if=\"menuItem.notification\"\n class=\"absolute top-0.25 right-0.25 size-1.5 rounded-full bg-red-600\"\n ></span>\n </div>\n\n <Transition\n enter-active-class=\"transition-all duration-300 ease-out\"\n enter-from-class=\"opacity-0 -translate-x-2\"\n enter-to-class=\"opacity-100 translate-x-0\"\n leave-active-class=\"transition-all duration-200 ease-in\"\n leave-from-class=\"opacity-100 translate-x-0\"\n leave-to-class=\"opacity-0 -translate-x-2\"\n >\n <span\n v-if=\"expanded\"\n :class=\"\n isRouteActive\n ? 'text-white'\n : 'text-white/50 group-hover:text-white/80 dark:text-gray-700 dark:group-hover:text-gray-500'\n \"\n class=\"text-sm font-semibold whitespace-nowrap\"\n >{{ menuItem.label }}</span\n >\n </Transition>\n </component>\n</template>\n","<script lang=\"ts\" setup>\nimport { ref, computed, onUnmounted, nextTick, watch } from 'vue'\nimport { Icon } from '@iconify/vue'\n\nexport interface DropdownItem {\n key: string\n label: string\n icon?: string\n disabled?: boolean\n danger?: boolean\n divider?: boolean\n}\n\nconst props = withDefaults(\n defineProps<{\n /** Dropdown items (optional if using default slot) */\n items?: DropdownItem[]\n /** Align dropdown */\n align?: 'left' | 'right'\n /** Dropdown width */\n width?: 'auto' | 'full' | 'sm' | 'md' | 'lg'\n /** Use teleport to body to avoid overflow clipping */\n teleport?: boolean\n }>(),\n {\n items: () => [],\n align: 'left',\n width: 'auto',\n teleport: true,\n },\n)\n\nconst emit = defineEmits<{\n select: [item: DropdownItem]\n}>()\n\nconst isOpen = ref(false)\nconst dropdownRef = ref<HTMLElement>()\nconst triggerRef = ref<HTMLElement>()\nconst menuRef = ref<HTMLElement>()\nconst dropdownPosition = ref({ top: 0, left: 0, width: 0 })\n\nconst updatePosition = () => {\n if (!triggerRef.value || !props.teleport) return\n const rect = triggerRef.value.getBoundingClientRect()\n dropdownPosition.value = {\n top: rect.bottom + window.scrollY + 8,\n left: props.align === 'right' ? rect.right + window.scrollX : rect.left + window.scrollX,\n width: rect.width,\n }\n}\n\nconst toggle = () => {\n isOpen.value = !isOpen.value\n if (isOpen.value) {\n nextTick(updatePosition)\n }\n}\n\nconst close = () => {\n isOpen.value = false\n}\n\nconst selectItem = (item: DropdownItem) => {\n if (item.disabled || item.divider) return\n emit('select', item)\n close()\n}\n\nconst handleClickOutside = (event: MouseEvent) => {\n const target = event.target as Node\n const isInsideTrigger = triggerRef.value?.contains(target)\n const isInsideMenu = menuRef.value?.contains(target)\n if (!isInsideTrigger && !isInsideMenu) {\n close()\n }\n}\n\nwatch(isOpen, (newValue) => {\n if (newValue) {\n document.addEventListener('click', handleClickOutside)\n window.addEventListener('scroll', updatePosition, true)\n window.addEventListener('resize', updatePosition)\n } else {\n document.removeEventListener('click', handleClickOutside)\n window.removeEventListener('scroll', updatePosition, true)\n window.removeEventListener('resize', updatePosition)\n }\n})\n\nonUnmounted(() => {\n document.removeEventListener('click', handleClickOutside)\n window.removeEventListener('scroll', updatePosition, true)\n window.removeEventListener('resize', updatePosition)\n})\n\nconst widthClasses = {\n auto: 'w-auto min-w-[10rem]',\n full: 'w-full',\n sm: 'w-32',\n md: 'w-48',\n lg: 'w-64',\n}\n\nconst dropdownStyle = computed(() => {\n if (!props.teleport) return {}\n return {\n position: 'absolute' as const,\n top: `${dropdownPosition.value.top}px`,\n left: props.align === 'right' ? 'auto' : `${dropdownPosition.value.left}px`,\n right: props.align === 'right' ? `${window.innerWidth - dropdownPosition.value.left - dropdownPosition.value.width}px` : 'auto',\n }\n})\n</script>\n\n<template>\n <div ref=\"dropdownRef\" class=\"relative inline-block\">\n <div ref=\"triggerRef\" @click=\"toggle\">\n <slot name=\"trigger\">\n <button\n type=\"button\"\n class=\"inline-flex items-center gap-2 rounded-lg border border-gray-300 bg-white px-4 py-2 text-sm font-medium text-gray-700 hover:bg-gray-50 dark:border-gray-600 dark:bg-gray-800 dark:text-gray-200 dark:hover:bg-gray-700\"\n >\n <slot name=\"trigger-label\">Options</slot>\n <Icon\n icon=\"lucide:chevron-down\"\n :class=\"['size-4 transition-transform', isOpen && 'rotate-180']\"\n />\n </button>\n </slot>\n </div>\n\n <Teleport to=\"body\" :disabled=\"!teleport\">\n <Transition\n enter-active-class=\"transition ease-out duration-100\"\n enter-from-class=\"transform opacity-0 scale-95\"\n enter-to-class=\"transform opacity-100 scale-100\"\n leave-active-class=\"transition ease-in duration-75\"\n leave-from-class=\"transform opacity-100 scale-100\"\n leave-to-class=\"transform opacity-0 scale-95\"\n >\n <div\n v-if=\"isOpen\"\n ref=\"menuRef\"\n :style=\"dropdownStyle\"\n :class=\"[\n 'z-[9999] rounded-lg border border-gray-200 bg-white py-1 shadow-lg dark:border-gray-700 dark:bg-gray-800',\n widthClasses[width],\n !teleport && (align === 'right' ? 'absolute mt-2 right-0' : 'absolute mt-2 left-0'),\n ]\"\n >\n <!-- Custom content via default slot -->\n <slot :close=\"close\">\n <!-- Default items rendering -->\n <template v-for=\"item in items\" :key=\"item.key\">\n <div\n v-if=\"item.divider\"\n class=\"my-1 border-t border-gray-200 dark:border-gray-700\"\n />\n <button\n v-else\n type=\"button\"\n :disabled=\"item.disabled\"\n :class=\"[\n 'flex w-full items-center gap-2 px-4 py-2 text-left text-sm transition-colors',\n item.disabled\n ? 'cursor-not-allowed opacity-50'\n : item.danger\n ? 'text-red-600 hover:bg-red-50 dark:text-red-400 dark:hover:bg-red-900/20'\n : 'text-gray-700 hover:bg-gray-100 dark:text-gray-200 dark:hover:bg-gray-700',\n ]\"\n @click=\"selectItem(item)\"\n >\n <Icon v-if=\"item.icon\" :icon=\"item.icon\" class=\"size-4\" />\n {{ item.label }}\n </button>\n </template>\n </slot>\n </div>\n </Transition>\n </Teleport>\n </div>\n</template>\n"],"names":["_openBlock","_createBlock","_resolveDynamicComponent","_mergeProps","_createElementVNode","_hoisted_1","_createVNode","_unref","_normalizeClass","_createElementBlock","_hoisted_2","_Transition","_toDisplayString","_renderSlot","_Teleport","_Fragment","_renderList"],"mappings":"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"}
|