@necrolab/dashboard 0.4.61 → 0.4.208
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/.prettierrc +1 -27
- package/.vscode/extensions.json +1 -1
- package/README.md +79 -43
- package/backend/api.js +48 -40
- package/backend/auth.js +3 -3
- package/backend/batching.js +1 -1
- package/backend/endpoints.js +77 -13
- package/backend/index.js +2 -2
- package/backend/mock-data.js +38 -29
- package/backend/mock-src/classes/logger.js +8 -8
- package/backend/mock-src/classes/utils.js +3 -7
- package/backend/mock-src/database.js +0 -0
- package/backend/mock-src/ticketmaster.js +79 -79
- package/backend/validator.js +2 -2
- package/config/configs.json +3 -2
- package/config/filter.json +3 -2
- package/index.html +10 -81
- package/index.js +1 -1
- package/package.json +25 -40
- package/postcss.config.js +1 -1
- package/postinstall.js +17 -98
- package/public/android-chrome-192x192.png +0 -0
- package/public/android-chrome-512x512.png +0 -0
- package/public/apple-touch-icon.png +0 -0
- package/public/favicon-16x16.png +0 -0
- package/public/favicon-32x32.png +0 -0
- package/public/favicon.ico +0 -0
- package/public/manifest.json +7 -12
- package/public/sw.js +2 -0
- package/public/workbox-49fdaf31.js +2 -0
- package/public/workbox-49fdaf31.js.map +1 -0
- package/public/workbox-88575b92.js +2 -0
- package/public/workbox-88575b92.js.map +1 -0
- package/public/workbox-a67a7b11.js +2 -0
- package/public/workbox-a67a7b11.js.map +1 -0
- package/public/workbox-d4314735.js +2 -0
- package/public/workbox-d4314735.js.map +1 -0
- package/public/workbox-e0f89ef3.js +2 -0
- package/public/workbox-e0f89ef3.js.map +1 -0
- package/run +9 -176
- package/src/App.vue +85 -498
- package/src/assets/css/_input.scss +99 -144
- package/src/assets/css/main.scss +99 -450
- package/src/assets/img/background.svg +2 -2
- package/src/assets/img/logo_icon.png +0 -0
- package/src/components/Auth/LoginForm.vue +11 -62
- package/src/components/Editors/Account/Account.vue +40 -116
- package/src/components/Editors/Account/AccountCreator.vue +39 -88
- package/src/components/Editors/Account/AccountView.vue +34 -102
- package/src/components/Editors/Account/CreateAccount.vue +32 -80
- package/src/components/Editors/Profile/CreateProfile.vue +83 -269
- package/src/components/Editors/Profile/Profile.vue +47 -132
- package/src/components/Editors/Profile/ProfileCountryChooser.vue +20 -82
- package/src/components/Editors/Profile/ProfileView.vue +34 -91
- package/src/components/Editors/TagLabel.vue +6 -67
- package/src/components/Filter/Filter.vue +72 -289
- package/src/components/Filter/FilterPreview.vue +30 -171
- package/src/components/Filter/PriceSortToggle.vue +4 -74
- package/src/components/Table/Header.vue +1 -1
- package/src/components/Table/Row.vue +1 -1
- package/src/components/Table/Table.vue +2 -19
- package/src/components/Tasks/CheckStock.vue +13 -28
- package/src/components/Tasks/Controls/DesktopControls.vue +17 -17
- package/src/components/Tasks/Controls/MobileControls.vue +45 -8
- package/src/components/Tasks/CreateTaskAXS.vue +73 -79
- package/src/components/Tasks/CreateTaskTM.vue +142 -94
- package/src/components/Tasks/MassEdit.vue +7 -9
- package/src/components/Tasks/QuickSettings.vue +55 -169
- package/src/components/Tasks/ScrapeVenue.vue +4 -7
- package/src/components/Tasks/Stats.vue +23 -52
- package/src/components/Tasks/Task.vue +136 -378
- package/src/components/Tasks/TaskView.vue +47 -107
- package/src/components/Tasks/Utilities.vue +6 -5
- package/src/components/icons/Bag.vue +1 -1
- package/src/components/icons/Loyalty.vue +1 -1
- package/src/components/icons/Mail.vue +2 -2
- package/src/components/icons/Play.vue +2 -2
- package/src/components/icons/Reload.vue +5 -4
- package/src/components/icons/Sandclock.vue +2 -2
- package/src/components/icons/Stadium.vue +1 -1
- package/src/components/icons/index.js +1 -24
- package/src/components/ui/Modal.vue +13 -105
- package/src/components/ui/Navbar.vue +38 -171
- package/src/components/ui/ReconnectIndicator.vue +55 -351
- package/src/components/ui/Splash.vue +35 -5
- package/src/components/ui/controls/CountryChooser.vue +62 -200
- package/src/components/ui/controls/atomic/Checkbox.vue +10 -119
- package/src/components/ui/controls/atomic/Dropdown.vue +39 -208
- package/src/components/ui/controls/atomic/MultiDropdown.vue +37 -300
- package/src/libs/Filter.js +170 -200
- package/src/registerServiceWorker.js +1 -1
- package/src/stores/connection.js +53 -51
- package/src/stores/logger.js +3 -11
- package/src/stores/sampleData.js +235 -207
- package/src/stores/ui.js +44 -112
- package/src/stores/utils.js +6 -90
- package/src/views/Accounts.vue +35 -44
- package/src/views/Console.vue +90 -341
- package/src/views/Editor.vue +123 -1176
- package/src/views/FilterBuilder.vue +251 -607
- package/src/views/Login.vue +14 -76
- package/src/views/Profiles.vue +25 -44
- package/src/views/Tasks.vue +100 -187
- package/static/offline.html +50 -192
- package/tailwind.config.js +26 -104
- package/vite.config.js +16 -73
- package/vue.config.js +32 -0
- package/workbox-config.js +11 -0
- package/artwork/image.png +0 -0
- package/dev-server.js +0 -136
- package/exit +0 -209
- package/jsconfig.json +0 -16
- package/src/assets/css/_utilities.scss +0 -388
- package/src/assets/img/background.svg.backup +0 -11
- package/src/components/icons/Check.vue +0 -5
- package/src/components/icons/Close.vue +0 -21
- package/src/components/icons/CloseX.vue +0 -5
- package/src/components/icons/Key.vue +0 -21
- package/src/components/icons/Pencil.vue +0 -21
- package/src/components/icons/Profile.vue +0 -18
- package/src/components/icons/Sell.vue +0 -21
- package/src/components/icons/Spinner.vue +0 -42
- package/src/components/icons/SquareCheck.vue +0 -18
- package/src/components/icons/SquareUncheck.vue +0 -18
- package/src/components/icons/Wildcard.vue +0 -18
- package/src/components/ui/controls/atomic/LoadingButton.vue +0 -45
- package/src/composables/useClickOutside.js +0 -21
- package/src/composables/useDropdownPosition.js +0 -174
- package/src/types/index.js +0 -41
- package/src/utils/debug.js +0 -1
- package/switch-branch.sh +0 -41
- package/workbox-config.cjs +0 -63
- /package/src/assets/img/{logo_icon-old.png → logo_icon_2.png} +0 -0
|
@@ -1,44 +1,37 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div @click="toggleOpened" class="dropdown
|
|
3
|
-
<span class="
|
|
4
|
-
<span
|
|
5
|
-
|
|
6
|
-
</span>
|
|
7
|
-
<DownIcon
|
|
2
|
+
<div @click="toggleOpened" class="relative dropdown w-full p-2 h-10 text-white ml-auto rounded ring-0">
|
|
3
|
+
<span class="gap-3 justify-between items-center z-inf">
|
|
4
|
+
<span style="width: full" :class="`overflow-hidden block ${capitalize ? 'capitalize' : ''}`">{{
|
|
5
|
+
currentValue ? currentValue : props.default
|
|
6
|
+
}}</span>
|
|
7
|
+
<DownIcon :class="`absolute ${props.rightAmount || 'right-4'} top-3.5`" />
|
|
8
8
|
</span>
|
|
9
|
-
<
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
</button>
|
|
29
|
-
</div>
|
|
30
|
-
</transition>
|
|
31
|
-
</Teleport>
|
|
9
|
+
<div
|
|
10
|
+
class="dropdown-content z-inf custom-dropdown-content max-h-40 overflow-y-auto hidden-scrollbars"
|
|
11
|
+
v-if="opened"
|
|
12
|
+
>
|
|
13
|
+
<div class="grid grid-rows-1">
|
|
14
|
+
<button
|
|
15
|
+
v-bind:key="f"
|
|
16
|
+
:class="` cursor-pointer text-left w-full ${
|
|
17
|
+
i !== 0 ? `border-t ${props.topPadding || 'pt-3 mt-3'}` : ''
|
|
18
|
+
} border-light-300`"
|
|
19
|
+
v-for="(f, i) in !allowDefault ? props.options : ['', ...props.options]"
|
|
20
|
+
@click="chose(f)"
|
|
21
|
+
>
|
|
22
|
+
<span :class="`overflow-hidden smooth-hover ${capitalize ? 'capitalize' : ''}`">{{
|
|
23
|
+
f ? f : props.default
|
|
24
|
+
}}</span>
|
|
25
|
+
</button>
|
|
26
|
+
</div>
|
|
27
|
+
</div>
|
|
32
28
|
</div>
|
|
33
29
|
</template>
|
|
34
30
|
|
|
35
31
|
<script setup>
|
|
36
|
-
import { ref, computed
|
|
37
|
-
import { DownIcon
|
|
32
|
+
import { ref, computed } from "vue";
|
|
33
|
+
import { DownIcon } from "@/components/icons";
|
|
38
34
|
import { useUIStore } from "@/stores/ui";
|
|
39
|
-
import { useDropdownPosition } from "@/composables/useDropdownPosition";
|
|
40
|
-
import { useClickOutside } from "@/composables/useClickOutside";
|
|
41
|
-
|
|
42
35
|
const ui = useUIStore();
|
|
43
36
|
|
|
44
37
|
const props = defineProps({
|
|
@@ -49,197 +42,35 @@ const props = defineProps({
|
|
|
49
42
|
allowDefault: { type: Boolean },
|
|
50
43
|
rightAmount: { type: String },
|
|
51
44
|
topPadding: { type: String },
|
|
52
|
-
capitalize: { type: Boolean }
|
|
53
|
-
includeAdjacentButtons: { type: Boolean, default: false },
|
|
54
|
-
variant: { type: String, default: "default" }
|
|
45
|
+
capitalize: { type: Boolean }
|
|
55
46
|
});
|
|
56
47
|
|
|
57
48
|
const currentValue = ref(props.value);
|
|
58
|
-
const dropdownRef = ref(null);
|
|
59
|
-
|
|
60
|
-
// Watch for changes to the value prop and update currentValue
|
|
61
|
-
watch(
|
|
62
|
-
() => props.value,
|
|
63
|
-
(newValue) => {
|
|
64
|
-
currentValue.value = newValue;
|
|
65
|
-
}
|
|
66
|
-
);
|
|
67
|
-
|
|
68
49
|
const id = Math.random();
|
|
69
50
|
const opened = computed(() => ui.currentDropdown === id);
|
|
70
51
|
|
|
71
|
-
// Use composables for positioning and click outside
|
|
72
|
-
const { menuStyle, updatePosition } = useDropdownPosition(dropdownRef, {
|
|
73
|
-
maxHeight: 200,
|
|
74
|
-
includeAdjacentButtons: props.includeAdjacentButtons,
|
|
75
|
-
estimateHeight: () => {
|
|
76
|
-
const optionsCount = !props.allowDefault ? props.options?.length || 0 : (props.options?.length || 0) + 1;
|
|
77
|
-
return Math.min(optionsCount * 44, 200);
|
|
78
|
-
}
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
useClickOutside(dropdownRef, () => {
|
|
82
|
-
if (opened.value) {
|
|
83
|
-
ui.setCurrentDropdown("");
|
|
84
|
-
}
|
|
85
|
-
});
|
|
86
|
-
|
|
87
52
|
const toggleOpened = () => {
|
|
88
|
-
if (opened.value)
|
|
89
|
-
|
|
90
|
-
} else {
|
|
91
|
-
ui.setCurrentDropdown(id);
|
|
92
|
-
updatePosition();
|
|
93
|
-
}
|
|
53
|
+
if (opened.value) ui.setCurrentDropdown("");
|
|
54
|
+
else ui.setCurrentDropdown(id);
|
|
94
55
|
};
|
|
95
56
|
|
|
96
57
|
const chose = (f) => {
|
|
97
58
|
ui.logger.Info("Dropdown: chosen", f, "hiding...");
|
|
98
59
|
currentValue.value = f;
|
|
99
|
-
|
|
100
|
-
if (props.onClick) props.onClick(f);
|
|
101
|
-
// Ensure dropdown closes
|
|
60
|
+
opened.value = false;
|
|
102
61
|
setTimeout(() => {
|
|
103
|
-
|
|
62
|
+
opened.value = false;
|
|
63
|
+
ui.logger.Info("Showing dropdown again");
|
|
104
64
|
}, 50);
|
|
65
|
+
props.onClick(f);
|
|
105
66
|
};
|
|
106
67
|
</script>
|
|
107
68
|
|
|
108
69
|
<style scoped>
|
|
109
|
-
.dropdown {
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
.dropdown.transparent {
|
|
117
|
-
background: transparent !important;
|
|
118
|
-
border: none !important;
|
|
119
|
-
box-shadow: none !important;
|
|
120
|
-
padding: 0 !important;
|
|
121
|
-
height: 40px !important;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
.dropdown.transparent:hover,
|
|
125
|
-
.dropdown.transparent:focus-within {
|
|
126
|
-
border: none !important;
|
|
127
|
-
background: transparent !important;
|
|
128
|
-
box-shadow: none !important;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
.dropdown:not(.transparent):hover {
|
|
132
|
-
@apply border-dark-400;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
.dropdown:not(.transparent):focus-within {
|
|
136
|
-
@apply border-blue-500;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
@media (min-width: 768px) {
|
|
140
|
-
.dropdown {
|
|
141
|
-
height: 40px;
|
|
142
|
-
padding: 0.625rem;
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
.dropdown-display {
|
|
147
|
-
@apply flex items-center justify-between z-10 w-full h-full;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
.dropdown-value {
|
|
151
|
-
@apply overflow-hidden truncate min-w-0 flex-1 mr-2 text-sm;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
@media (min-width: 768px) {
|
|
155
|
-
.dropdown-value {
|
|
156
|
-
font-size: 12px;
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
.dropdown-arrow {
|
|
161
|
-
@apply w-4 h-4 transition-all duration-300 flex-shrink-0;
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
.dropdown-menu-portal {
|
|
165
|
-
@apply rounded-xl shadow-2xl overflow-hidden;
|
|
166
|
-
background: linear-gradient(135deg, #2a2b30 0%, #2e2f34 100%);
|
|
167
|
-
border: 1px solid #3d3e44;
|
|
168
|
-
backdrop-filter: blur(12px);
|
|
169
|
-
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.4), 0 10px 10px -5px rgba(0, 0, 0, 0.2),
|
|
170
|
-
0 0 0 1px rgba(255, 255, 255, 0.05);
|
|
171
|
-
overflow-y: auto !important;
|
|
172
|
-
overscroll-behavior: contain !important;
|
|
173
|
-
touch-action: pan-y !important;
|
|
174
|
-
-webkit-overflow-scrolling: touch !important;
|
|
175
|
-
scrollbar-width: none;
|
|
176
|
-
-ms-overflow-style: none;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
.dropdown-menu-portal::-webkit-scrollbar {
|
|
180
|
-
display: none;
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
.dropdown-item {
|
|
184
|
-
@apply cursor-pointer text-left w-full text-white transition-all duration-200 flex items-center justify-between;
|
|
185
|
-
padding: 0.75rem 1rem;
|
|
186
|
-
font-size: 0.875rem;
|
|
187
|
-
font-weight: 500;
|
|
188
|
-
border-bottom: 1px solid rgba(61, 62, 68, 0.3);
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
.dropdown-item:last-child {
|
|
192
|
-
border-bottom: none;
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
.dropdown-item:hover {
|
|
196
|
-
@apply bg-dark-600;
|
|
197
|
-
color: #ffffff;
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
.dropdown-item:active {
|
|
201
|
-
@apply bg-dark-650;
|
|
202
|
-
box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.4);
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
.dropdown-item:first-child {
|
|
206
|
-
@apply rounded-t-xl;
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
.dropdown-item:last-child {
|
|
210
|
-
@apply rounded-b-xl;
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
.dropdown-item-text {
|
|
214
|
-
@apply overflow-hidden truncate pr-2;
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
.dropdown-item svg {
|
|
218
|
-
@apply w-4 h-4;
|
|
219
|
-
color: #10b981;
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
.dropdown-fade-enter-active {
|
|
223
|
-
@apply transition-all duration-300;
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
.dropdown-fade-leave-active {
|
|
227
|
-
@apply transition-all duration-200;
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
.dropdown-fade-enter-from {
|
|
231
|
-
@apply opacity-0;
|
|
232
|
-
transform: translateY(-8px) scale(0.95);
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
.dropdown-fade-leave-to {
|
|
236
|
-
@apply opacity-0;
|
|
237
|
-
transform: translateY(-4px) scale(0.98);
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
.dropdown-fade-enter-to,
|
|
241
|
-
.dropdown-fade-leave-from {
|
|
242
|
-
@apply opacity-100;
|
|
243
|
-
transform: translateY(0) scale(1);
|
|
70
|
+
.custom-dropdown-content {
|
|
71
|
+
top: 2.5rem !important;
|
|
72
|
+
@apply border border-light-300;
|
|
73
|
+
left: -1px !important;
|
|
74
|
+
width: calc(100% + 2px);
|
|
244
75
|
}
|
|
245
76
|
</style>
|
|
@@ -1,55 +1,31 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div @click="toggleOpened" class="dropdown
|
|
3
|
-
<span class="
|
|
4
|
-
<span
|
|
2
|
+
<div @click="toggleOpened" class="relative dropdown w-full p-2 h-10 text-white ml-auto rounded ring-0 px-3.5">
|
|
3
|
+
<span class="gap-3 justify-between items-center z-inf">
|
|
4
|
+
<span style="width: full" :class="`overflow-hidden block ${capitalize ? 'capitalize' : ''}`">
|
|
5
5
|
{{ displayValue }}
|
|
6
6
|
</span>
|
|
7
|
-
<
|
|
8
|
-
<span v-if="selectedOptions.length > 1" class="counter-badge">
|
|
9
|
-
{{ selectedOptions.length }}
|
|
10
|
-
</span>
|
|
11
|
-
<DownIcon class="dropdown-arrow" :class="opened ? 'rotate-180' : ''" />
|
|
12
|
-
</div>
|
|
7
|
+
<DownIcon :class="`absolute ${props.rightAmount || 'right-3'} top-3.5`" />
|
|
13
8
|
</span>
|
|
14
|
-
<
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
</button>
|
|
35
|
-
</div>
|
|
36
|
-
|
|
37
|
-
<div v-if="selectedOptions.length > 0" class="selected-summary">
|
|
38
|
-
<div class="flex items-center justify-between">
|
|
39
|
-
<div class="selected-count">
|
|
40
|
-
<span class="count-badge">
|
|
41
|
-
{{ selectedOptions.length }}
|
|
42
|
-
</span>
|
|
43
|
-
<span class="count-label">
|
|
44
|
-
item{{ selectedOptions.length === 1 ? "" : "s" }} selected
|
|
45
|
-
</span>
|
|
46
|
-
</div>
|
|
47
|
-
<button class="clear-button" @click.stop="clearAll">Clear All</button>
|
|
48
|
-
</div>
|
|
49
|
-
</div>
|
|
50
|
-
</div>
|
|
51
|
-
</transition>
|
|
52
|
-
</Teleport>
|
|
9
|
+
<div
|
|
10
|
+
class="dropdown-content z-inf custom-dropdown-content max-h-40 overflow-y-auto hidden-scrollbars"
|
|
11
|
+
v-if="opened"
|
|
12
|
+
>
|
|
13
|
+
<div class="grid grid-rows-1">
|
|
14
|
+
<button
|
|
15
|
+
v-for="(option, i) in props.options"
|
|
16
|
+
:key="option.value"
|
|
17
|
+
:class="`cursor-pointer text-left w-full ${
|
|
18
|
+
i !== 0 ? `border-t ${props.topPadding || 'pt-3 mt-3'}` : ''
|
|
19
|
+
} border-light-300 flex justify-between items-center`"
|
|
20
|
+
@click.stop="toggleOption(option.value)"
|
|
21
|
+
>
|
|
22
|
+
<span :class="`overflow-hidden smooth-hover ${capitalize ? 'capitalize' : ''}`">
|
|
23
|
+
{{ option.label }}
|
|
24
|
+
</span>
|
|
25
|
+
<span v-if="selectedOptions.includes(option.value)" class="ml-2 scale-125"><CheckmarkIcon /></span>
|
|
26
|
+
</button>
|
|
27
|
+
</div>
|
|
28
|
+
</div>
|
|
53
29
|
</div>
|
|
54
30
|
</template>
|
|
55
31
|
|
|
@@ -57,8 +33,6 @@
|
|
|
57
33
|
import { ref, computed } from "vue";
|
|
58
34
|
import { DownIcon, CheckmarkIcon } from "@/components/icons";
|
|
59
35
|
import { useUIStore } from "@/stores/ui";
|
|
60
|
-
import { useDropdownPosition } from "@/composables/useDropdownPosition";
|
|
61
|
-
import { useClickOutside } from "@/composables/useClickOutside";
|
|
62
36
|
|
|
63
37
|
const ui = useUIStore();
|
|
64
38
|
|
|
@@ -68,64 +42,23 @@ const props = defineProps({
|
|
|
68
42
|
options: { type: Array, required: true },
|
|
69
43
|
rightAmount: { type: String },
|
|
70
44
|
topPadding: { type: String },
|
|
71
|
-
capitalize: { type: Boolean }
|
|
72
|
-
includeAdjacentButtons: { type: Boolean, default: false }
|
|
45
|
+
capitalize: { type: Boolean }
|
|
73
46
|
});
|
|
74
47
|
|
|
75
48
|
const selectedOptions = ref([]);
|
|
76
|
-
const dropdownRef = ref(null);
|
|
77
49
|
const id = Math.random();
|
|
78
50
|
const opened = computed(() => ui.currentDropdown === id);
|
|
79
51
|
|
|
80
52
|
const displayValue = computed(() => {
|
|
81
53
|
if (selectedOptions.value.length === 0) {
|
|
82
|
-
return props.default
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
if (selectedOptions.value.length === 1) {
|
|
86
|
-
const option = props.options.find((opt) => opt.value === selectedOptions.value[0]);
|
|
87
|
-
return option ? option.label : selectedOptions.value[0];
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
if (selectedOptions.value.length <= 2) {
|
|
91
|
-
const labels = selectedOptions.value.map((val) => {
|
|
92
|
-
const option = props.options.find((opt) => opt.value === val);
|
|
93
|
-
return option ? option.label : val;
|
|
94
|
-
});
|
|
95
|
-
return labels.join(", ");
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
const firstOption = props.options.find((opt) => opt.value === selectedOptions.value[0]);
|
|
99
|
-
const firstName = firstOption ? firstOption.label : selectedOptions.value[0];
|
|
100
|
-
return `${firstName} +${selectedOptions.value.length - 1} more`;
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
// Use composables for positioning and click outside
|
|
104
|
-
const { menuStyle, updatePosition } = useDropdownPosition(dropdownRef, {
|
|
105
|
-
maxHeight: 280,
|
|
106
|
-
includeAdjacentButtons: props.includeAdjacentButtons,
|
|
107
|
-
estimateHeight: () => {
|
|
108
|
-
const optionsCount = props.options?.length || 0;
|
|
109
|
-
const summaryHeight = selectedOptions.value.length > 0 ? 70 : 0;
|
|
110
|
-
const baseMaxHeight = selectedOptions.value.length > 0 ? 280 : 200;
|
|
111
|
-
const optionListHeight = Math.min(optionsCount * 44, 200);
|
|
112
|
-
return Math.min(optionListHeight + summaryHeight, baseMaxHeight);
|
|
113
|
-
}
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
useClickOutside(dropdownRef, () => {
|
|
117
|
-
if (opened.value) {
|
|
118
|
-
ui.setCurrentDropdown("");
|
|
54
|
+
return props.default;
|
|
119
55
|
}
|
|
56
|
+
return selectedOptions.value.join(", ");
|
|
120
57
|
});
|
|
121
58
|
|
|
122
59
|
const toggleOpened = () => {
|
|
123
|
-
if (opened.value)
|
|
124
|
-
|
|
125
|
-
} else {
|
|
126
|
-
ui.setCurrentDropdown(id);
|
|
127
|
-
updatePosition();
|
|
128
|
-
}
|
|
60
|
+
if (opened.value) ui.setCurrentDropdown("");
|
|
61
|
+
else ui.setCurrentDropdown(id);
|
|
129
62
|
};
|
|
130
63
|
|
|
131
64
|
const toggleOption = (option) => {
|
|
@@ -135,46 +68,13 @@ const toggleOption = (option) => {
|
|
|
135
68
|
} else {
|
|
136
69
|
selectedOptions.value.splice(index, 1);
|
|
137
70
|
}
|
|
138
|
-
|
|
139
|
-
// Handle default logic
|
|
140
71
|
if (selectedOptions.value.length === 0 && props.default) {
|
|
141
72
|
selectedOptions.value = [props.default];
|
|
142
73
|
}
|
|
143
|
-
if (selectedOptions.value.length > 1 && selectedOptions.value.includes(props.default) && option !== props.default)
|
|
74
|
+
if (selectedOptions.value.length > 1 && selectedOptions.value.includes(props.default) && option !== props.default)
|
|
144
75
|
selectedOptions.value = selectedOptions.value.filter((e) => e !== props.default);
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
if (typeof props.onSelect === "function") {
|
|
150
|
-
props.onSelect(selectedOptions.value);
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
// Recalculate position after selection changes
|
|
154
|
-
updatePosition();
|
|
155
|
-
};
|
|
156
|
-
|
|
157
|
-
const clearAll = () => {
|
|
158
|
-
// Default to first option instead of empty
|
|
159
|
-
if (props.options && props.options.length > 0) {
|
|
160
|
-
selectedOptions.value = [props.options[0].value];
|
|
161
|
-
if (typeof props.onSelect === "function") {
|
|
162
|
-
props.onSelect([props.options[0].value]);
|
|
163
|
-
}
|
|
164
|
-
} else if (props.default) {
|
|
165
|
-
selectedOptions.value = [props.default];
|
|
166
|
-
if (typeof props.onSelect === "function") {
|
|
167
|
-
props.onSelect([props.default]);
|
|
168
|
-
}
|
|
169
|
-
} else {
|
|
170
|
-
selectedOptions.value = [];
|
|
171
|
-
if (typeof props.onSelect === "function") {
|
|
172
|
-
props.onSelect([]);
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
// Recalculate position after clearing
|
|
177
|
-
updatePosition();
|
|
76
|
+
else if (option == props.default) selectedOptions.value = [props.default];
|
|
77
|
+
if (typeof props.onSelect === "function") props.onSelect(selectedOptions.value);
|
|
178
78
|
};
|
|
179
79
|
|
|
180
80
|
// Initialize with default option if provided
|
|
@@ -184,173 +84,10 @@ if (props.default && !selectedOptions.value.includes(props.default)) {
|
|
|
184
84
|
</script>
|
|
185
85
|
|
|
186
86
|
<style scoped>
|
|
187
|
-
.dropdown {
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
.dropdown:hover {
|
|
195
|
-
@apply border-dark-400;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
.dropdown:focus-within {
|
|
199
|
-
@apply border-blue-500;
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
@media (max-width: 810px) {
|
|
203
|
-
.dropdown {
|
|
204
|
-
@apply h-10;
|
|
205
|
-
padding: 0.625rem;
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
.dropdown-display {
|
|
210
|
-
@apply flex items-center justify-between z-10;
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
.dropdown-value {
|
|
214
|
-
@apply w-full overflow-hidden block truncate pr-2 text-sm;
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
.dropdown-counter {
|
|
218
|
-
@apply flex items-center gap-2 absolute right-2;
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
.counter-badge {
|
|
222
|
-
@apply bg-green-500 text-white text-xs font-semibold px-1.5 py-0.5 rounded-full text-center min-w-[18px] shadow-sm;
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
.dropdown-arrow {
|
|
226
|
-
@apply min-w-4 min-h-4 transition-all duration-300;
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
.dropdown-menu-portal {
|
|
230
|
-
@apply rounded-xl shadow-2xl overflow-hidden;
|
|
231
|
-
background: linear-gradient(135deg, #2a2b30 0%, #2e2f34 100%);
|
|
232
|
-
border: 1px solid #3d3e44;
|
|
233
|
-
backdrop-filter: blur(12px);
|
|
234
|
-
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.4), 0 10px 10px -5px rgba(0, 0, 0, 0.2),
|
|
235
|
-
0 0 0 1px rgba(255, 255, 255, 0.05);
|
|
236
|
-
overscroll-behavior: contain !important;
|
|
237
|
-
touch-action: pan-y !important;
|
|
238
|
-
-webkit-overflow-scrolling: touch !important;
|
|
239
|
-
scrollbar-width: thin;
|
|
240
|
-
scrollbar-color: #4b5563 transparent;
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
.dropdown-menu-portal::-webkit-scrollbar {
|
|
244
|
-
width: 6px;
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
.dropdown-menu-portal::-webkit-scrollbar-track {
|
|
248
|
-
background: transparent;
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
.dropdown-menu-portal::-webkit-scrollbar-thumb {
|
|
252
|
-
background: #4b5563;
|
|
253
|
-
border-radius: 3px;
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
.dropdown-menu-portal::-webkit-scrollbar-thumb:hover {
|
|
257
|
-
background: #6b7280;
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
.dropdown-menu-portal.multi .option-list {
|
|
261
|
-
@apply max-h-48 overflow-y-auto;
|
|
262
|
-
overscroll-behavior: contain !important;
|
|
263
|
-
touch-action: pan-y !important;
|
|
264
|
-
-webkit-overflow-scrolling: touch !important;
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
.dropdown-item {
|
|
268
|
-
@apply cursor-pointer text-left w-full text-white transition-all duration-200 flex justify-between items-center;
|
|
269
|
-
padding: 0.75rem 1rem;
|
|
270
|
-
font-size: 0.875rem;
|
|
271
|
-
font-weight: 500;
|
|
272
|
-
border-bottom: 1px solid rgba(61, 62, 68, 0.3);
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
.dropdown-item:last-child {
|
|
276
|
-
border-bottom: none;
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
.dropdown-item:hover {
|
|
280
|
-
@apply bg-dark-600;
|
|
281
|
-
color: #ffffff;
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
.dropdown-item:active {
|
|
285
|
-
@apply bg-dark-650;
|
|
286
|
-
box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.4);
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
.dropdown-item:first-child {
|
|
290
|
-
@apply rounded-t-xl;
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
.dropdown-item:last-child {
|
|
294
|
-
@apply rounded-b-xl;
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
.dropdown-item-text {
|
|
298
|
-
@apply overflow-hidden;
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
/* Checkmark styling */
|
|
302
|
-
.dropdown-item svg {
|
|
303
|
-
@apply w-4 h-4;
|
|
304
|
-
color: #10b981;
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
.selected-summary {
|
|
308
|
-
@apply border-t bg-dark-550 w-full px-4 py-3;
|
|
309
|
-
border-top: 1px solid rgba(61, 62, 68, 0.5);
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
.selected-count {
|
|
313
|
-
@apply flex items-center gap-2;
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
.count-badge {
|
|
317
|
-
@apply bg-green-500 text-white text-xs font-semibold px-2 py-1 rounded-full shadow-sm;
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
.count-label {
|
|
321
|
-
@apply text-xs font-medium text-light-400;
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
.clear-button {
|
|
325
|
-
@apply text-xs bg-red-500 text-white transition-colors duration-200 font-medium px-3 py-1.5 rounded-lg shadow-sm;
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
.clear-button:hover {
|
|
329
|
-
@apply bg-red-400;
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
/* Transition animations */
|
|
333
|
-
.dropdown-fade-enter-active {
|
|
334
|
-
@apply transition-all duration-300;
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
.dropdown-fade-leave-active {
|
|
338
|
-
@apply transition-all duration-200;
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
.dropdown-fade-enter-from {
|
|
342
|
-
@apply opacity-0;
|
|
343
|
-
transform: translateY(-8px) scale(0.95);
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
.dropdown-fade-leave-to {
|
|
347
|
-
@apply opacity-0;
|
|
348
|
-
transform: translateY(-4px) scale(0.98);
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
.dropdown-fade-enter-to,
|
|
352
|
-
.dropdown-fade-leave-from {
|
|
353
|
-
@apply opacity-100;
|
|
354
|
-
transform: translateY(0) scale(1);
|
|
87
|
+
.custom-dropdown-content {
|
|
88
|
+
top: 2.5rem !important;
|
|
89
|
+
@apply border border-light-300 px-3.5;
|
|
90
|
+
left: -1px !important;
|
|
91
|
+
width: calc(100% + 2px);
|
|
355
92
|
}
|
|
356
93
|
</style>
|