@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.
Files changed (133) hide show
  1. package/.prettierrc +1 -27
  2. package/.vscode/extensions.json +1 -1
  3. package/README.md +79 -43
  4. package/backend/api.js +48 -40
  5. package/backend/auth.js +3 -3
  6. package/backend/batching.js +1 -1
  7. package/backend/endpoints.js +77 -13
  8. package/backend/index.js +2 -2
  9. package/backend/mock-data.js +38 -29
  10. package/backend/mock-src/classes/logger.js +8 -8
  11. package/backend/mock-src/classes/utils.js +3 -7
  12. package/backend/mock-src/database.js +0 -0
  13. package/backend/mock-src/ticketmaster.js +79 -79
  14. package/backend/validator.js +2 -2
  15. package/config/configs.json +3 -2
  16. package/config/filter.json +3 -2
  17. package/index.html +10 -81
  18. package/index.js +1 -1
  19. package/package.json +25 -40
  20. package/postcss.config.js +1 -1
  21. package/postinstall.js +17 -98
  22. package/public/android-chrome-192x192.png +0 -0
  23. package/public/android-chrome-512x512.png +0 -0
  24. package/public/apple-touch-icon.png +0 -0
  25. package/public/favicon-16x16.png +0 -0
  26. package/public/favicon-32x32.png +0 -0
  27. package/public/favicon.ico +0 -0
  28. package/public/manifest.json +7 -12
  29. package/public/sw.js +2 -0
  30. package/public/workbox-49fdaf31.js +2 -0
  31. package/public/workbox-49fdaf31.js.map +1 -0
  32. package/public/workbox-88575b92.js +2 -0
  33. package/public/workbox-88575b92.js.map +1 -0
  34. package/public/workbox-a67a7b11.js +2 -0
  35. package/public/workbox-a67a7b11.js.map +1 -0
  36. package/public/workbox-d4314735.js +2 -0
  37. package/public/workbox-d4314735.js.map +1 -0
  38. package/public/workbox-e0f89ef3.js +2 -0
  39. package/public/workbox-e0f89ef3.js.map +1 -0
  40. package/run +9 -176
  41. package/src/App.vue +85 -498
  42. package/src/assets/css/_input.scss +99 -144
  43. package/src/assets/css/main.scss +99 -450
  44. package/src/assets/img/background.svg +2 -2
  45. package/src/assets/img/logo_icon.png +0 -0
  46. package/src/components/Auth/LoginForm.vue +11 -62
  47. package/src/components/Editors/Account/Account.vue +40 -116
  48. package/src/components/Editors/Account/AccountCreator.vue +39 -88
  49. package/src/components/Editors/Account/AccountView.vue +34 -102
  50. package/src/components/Editors/Account/CreateAccount.vue +32 -80
  51. package/src/components/Editors/Profile/CreateProfile.vue +83 -269
  52. package/src/components/Editors/Profile/Profile.vue +47 -132
  53. package/src/components/Editors/Profile/ProfileCountryChooser.vue +20 -82
  54. package/src/components/Editors/Profile/ProfileView.vue +34 -91
  55. package/src/components/Editors/TagLabel.vue +6 -67
  56. package/src/components/Filter/Filter.vue +72 -289
  57. package/src/components/Filter/FilterPreview.vue +30 -171
  58. package/src/components/Filter/PriceSortToggle.vue +4 -74
  59. package/src/components/Table/Header.vue +1 -1
  60. package/src/components/Table/Row.vue +1 -1
  61. package/src/components/Table/Table.vue +2 -19
  62. package/src/components/Tasks/CheckStock.vue +13 -28
  63. package/src/components/Tasks/Controls/DesktopControls.vue +17 -17
  64. package/src/components/Tasks/Controls/MobileControls.vue +45 -8
  65. package/src/components/Tasks/CreateTaskAXS.vue +73 -79
  66. package/src/components/Tasks/CreateTaskTM.vue +142 -94
  67. package/src/components/Tasks/MassEdit.vue +7 -9
  68. package/src/components/Tasks/QuickSettings.vue +55 -169
  69. package/src/components/Tasks/ScrapeVenue.vue +4 -7
  70. package/src/components/Tasks/Stats.vue +23 -52
  71. package/src/components/Tasks/Task.vue +136 -378
  72. package/src/components/Tasks/TaskView.vue +47 -107
  73. package/src/components/Tasks/Utilities.vue +6 -5
  74. package/src/components/icons/Bag.vue +1 -1
  75. package/src/components/icons/Loyalty.vue +1 -1
  76. package/src/components/icons/Mail.vue +2 -2
  77. package/src/components/icons/Play.vue +2 -2
  78. package/src/components/icons/Reload.vue +5 -4
  79. package/src/components/icons/Sandclock.vue +2 -2
  80. package/src/components/icons/Stadium.vue +1 -1
  81. package/src/components/icons/index.js +1 -24
  82. package/src/components/ui/Modal.vue +13 -105
  83. package/src/components/ui/Navbar.vue +38 -171
  84. package/src/components/ui/ReconnectIndicator.vue +55 -351
  85. package/src/components/ui/Splash.vue +35 -5
  86. package/src/components/ui/controls/CountryChooser.vue +62 -200
  87. package/src/components/ui/controls/atomic/Checkbox.vue +10 -119
  88. package/src/components/ui/controls/atomic/Dropdown.vue +39 -208
  89. package/src/components/ui/controls/atomic/MultiDropdown.vue +37 -300
  90. package/src/libs/Filter.js +170 -200
  91. package/src/registerServiceWorker.js +1 -1
  92. package/src/stores/connection.js +53 -51
  93. package/src/stores/logger.js +3 -11
  94. package/src/stores/sampleData.js +235 -207
  95. package/src/stores/ui.js +44 -112
  96. package/src/stores/utils.js +6 -90
  97. package/src/views/Accounts.vue +35 -44
  98. package/src/views/Console.vue +90 -341
  99. package/src/views/Editor.vue +123 -1176
  100. package/src/views/FilterBuilder.vue +251 -607
  101. package/src/views/Login.vue +14 -76
  102. package/src/views/Profiles.vue +25 -44
  103. package/src/views/Tasks.vue +100 -187
  104. package/static/offline.html +50 -192
  105. package/tailwind.config.js +26 -104
  106. package/vite.config.js +16 -73
  107. package/vue.config.js +32 -0
  108. package/workbox-config.js +11 -0
  109. package/artwork/image.png +0 -0
  110. package/dev-server.js +0 -136
  111. package/exit +0 -209
  112. package/jsconfig.json +0 -16
  113. package/src/assets/css/_utilities.scss +0 -388
  114. package/src/assets/img/background.svg.backup +0 -11
  115. package/src/components/icons/Check.vue +0 -5
  116. package/src/components/icons/Close.vue +0 -21
  117. package/src/components/icons/CloseX.vue +0 -5
  118. package/src/components/icons/Key.vue +0 -21
  119. package/src/components/icons/Pencil.vue +0 -21
  120. package/src/components/icons/Profile.vue +0 -18
  121. package/src/components/icons/Sell.vue +0 -21
  122. package/src/components/icons/Spinner.vue +0 -42
  123. package/src/components/icons/SquareCheck.vue +0 -18
  124. package/src/components/icons/SquareUncheck.vue +0 -18
  125. package/src/components/icons/Wildcard.vue +0 -18
  126. package/src/components/ui/controls/atomic/LoadingButton.vue +0 -45
  127. package/src/composables/useClickOutside.js +0 -21
  128. package/src/composables/useDropdownPosition.js +0 -174
  129. package/src/types/index.js +0 -41
  130. package/src/utils/debug.js +0 -1
  131. package/switch-branch.sh +0 -41
  132. package/workbox-config.cjs +0 -63
  133. /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" :class="props.variant" ref="dropdownRef">
3
- <span class="dropdown-display">
4
- <span class="dropdown-value" :class="capitalize ? 'capitalize' : ''">
5
- {{ currentValue ? currentValue : props.default }}
6
- </span>
7
- <DownIcon class="dropdown-arrow" :class="opened ? 'rotate-180' : ''" />
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
- <Teleport to="body">
10
- <transition name="dropdown-fade">
11
- <div
12
- v-if="opened"
13
- class="dropdown-menu-portal scrollable"
14
- :style="menuStyle"
15
- @click.stop
16
- @wheel.stop
17
- @touchmove.stop>
18
- <button
19
- v-bind:key="f"
20
- class="dropdown-item"
21
- :class="i !== 0 ? 'border-t border-dark-650' : ''"
22
- v-for="(f, i) in !allowDefault ? props.options : ['', ...props.options]"
23
- @click.stop="chose(f)">
24
- <span class="dropdown-item-text" :class="capitalize ? 'capitalize' : ''">
25
- {{ f ? f : props.default }}
26
- </span>
27
- <CheckmarkIcon v-if="(f || props.default) === currentValue" class="ml-2" />
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, watch } from "vue";
37
- import { DownIcon, CheckmarkIcon } from "@/components/icons";
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
- ui.setCurrentDropdown("");
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
- ui.setCurrentDropdown("");
100
- if (props.onClick) props.onClick(f);
101
- // Ensure dropdown closes
60
+ opened.value = false;
102
61
  setTimeout(() => {
103
- ui.setCurrentDropdown("");
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
- @apply relative w-full text-white ml-auto rounded-lg ring-0 h-10;
111
- background: linear-gradient(135deg, #2a2b30 0%, #2e2f34 100%);
112
- border: 1px solid #3d3e44;
113
- padding: 0.75rem;
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" ref="dropdownRef">
3
- <span class="dropdown-display">
4
- <span class="dropdown-value" :class="capitalize ? 'capitalize' : ''">
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
- <div class="dropdown-counter">
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
- <Teleport to="body">
15
- <transition name="dropdown-fade">
16
- <div
17
- v-if="opened"
18
- class="dropdown-menu-portal multi scrollable"
19
- :style="menuStyle"
20
- @click.stop
21
- @wheel.stop
22
- @touchmove.stop>
23
- <div class="option-list scrollable">
24
- <button
25
- v-for="(option, i) in props.options"
26
- :key="option.value"
27
- class="dropdown-item"
28
- :class="i !== 0 ? 'border-t border-dark-650' : ''"
29
- @click.stop="toggleOption(option.value)">
30
- <span class="dropdown-item-text" :class="capitalize ? 'capitalize' : ''">
31
- {{ option.label }}
32
- </span>
33
- <CheckmarkIcon v-if="selectedOptions.includes(option.value)" class="ml-2" />
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 || "Select options...";
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
- ui.setCurrentDropdown("");
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
- } else if (option === props.default) {
146
- selectedOptions.value = [props.default];
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
- @apply relative w-full h-10 text-white ml-auto rounded-lg ring-0;
189
- background: linear-gradient(135deg, #2a2b30 0%, #2e2f34 100%);
190
- border: 1px solid #3d3e44;
191
- padding: 0.75rem;
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>