@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
package/src/App.vue
CHANGED
|
@@ -9,23 +9,27 @@
|
|
|
9
9
|
:style="{
|
|
10
10
|
'margin-top': maxPull() + 'px',
|
|
11
11
|
transform: `rotate(${ui.pullChange}deg)`
|
|
12
|
-
}"
|
|
12
|
+
}"
|
|
13
|
+
>
|
|
13
14
|
<div
|
|
14
15
|
class="refresh-icon p-2 rounded-full mx-auto duration-200"
|
|
15
16
|
:class="{
|
|
16
17
|
'opacity-100': ui.pullChange > 250,
|
|
17
18
|
'opacity-0': ui.pullChange < 250
|
|
18
|
-
}"
|
|
19
|
+
}"
|
|
20
|
+
>
|
|
19
21
|
<svg
|
|
20
22
|
xmlns="http://www.w3.org/2000/svg"
|
|
21
23
|
fill="none"
|
|
22
24
|
viewBox="0 0 24 24"
|
|
23
25
|
class="stroke-2 w-4 stroke-dark-400"
|
|
24
|
-
:class="{ 'opacity-0': ui.pullChange == 0 }"
|
|
26
|
+
:class="{ 'opacity-0': ui.pullChange == 0 }"
|
|
27
|
+
>
|
|
25
28
|
<path
|
|
26
29
|
strokeLinecap="round"
|
|
27
30
|
strokeLinejoin="round"
|
|
28
|
-
d="M16.023 9.348h4.992v-.001M2.985 19.644v-4.992m0 0h4.992m-4.993 0l3.181 3.183a8.25 8.25 0 0013.803-3.7M4.031 9.865a8.25 8.25 0 0113.803-3.7l3.181 3.182m0-4.991v4.99"
|
|
31
|
+
d="M16.023 9.348h4.992v-.001M2.985 19.644v-4.992m0 0h4.992m-4.993 0l3.181 3.183a8.25 8.25 0 0013.803-3.7M4.031 9.865a8.25 8.25 0 0113.803-3.7l3.181 3.182m0-4.991v4.99"
|
|
32
|
+
/>
|
|
29
33
|
</svg>
|
|
30
34
|
</div>
|
|
31
35
|
</div>
|
|
@@ -34,7 +38,8 @@
|
|
|
34
38
|
:class="{
|
|
35
39
|
'opacity-100': ui.pullChange > 250,
|
|
36
40
|
'opacity-0': ui.pullChange < 250
|
|
37
|
-
}"
|
|
41
|
+
}"
|
|
42
|
+
>
|
|
38
43
|
Release to refresh
|
|
39
44
|
</h2>
|
|
40
45
|
</div>
|
|
@@ -44,17 +49,12 @@
|
|
|
44
49
|
</div>
|
|
45
50
|
<div v-else key="main-components" class="flex">
|
|
46
51
|
<Navbar v-if="layout == 'dashboard'" class="fixed" />
|
|
47
|
-
<div :class="
|
|
48
|
-
<router-view v-slot="{ Component
|
|
49
|
-
<
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
:class="[
|
|
54
|
-
'component-container pb-2 mt-0 lg:mt-4 ios-wrapper',
|
|
55
|
-
{ 'w-full': landscapeIos }
|
|
56
|
-
]" />
|
|
57
|
-
</transition>
|
|
52
|
+
<div :class="`router-wrapper ${ui.pullChange > 1 ? '-mt-[60px]' : ''}`"></div>
|
|
53
|
+
<router-view v-slot="{ Component }">
|
|
54
|
+
<component
|
|
55
|
+
:is="Component"
|
|
56
|
+
:class="`component-container pb-2 mt-0 lg:mt-4 ios-wrapper ${landscapeIos ? 'w-full' : ''}`"
|
|
57
|
+
/>
|
|
58
58
|
</router-view>
|
|
59
59
|
</div>
|
|
60
60
|
</transition>
|
|
@@ -63,7 +63,7 @@
|
|
|
63
63
|
|
|
64
64
|
<script setup>
|
|
65
65
|
import { storeToRefs } from "pinia";
|
|
66
|
-
import { ref, computed, watch
|
|
66
|
+
import { ref, computed, watch } from "vue";
|
|
67
67
|
import { useRouter } from "vue-router";
|
|
68
68
|
import Navbar from "@/components/ui/Navbar.vue";
|
|
69
69
|
import { useUIStore } from "@/stores/ui";
|
|
@@ -88,50 +88,13 @@ function isIpadOS() {
|
|
|
88
88
|
return navigator.maxTouchPoints && navigator.maxTouchPoints > 2 && /MacIntel/.test(navigator.platform);
|
|
89
89
|
}
|
|
90
90
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
// For iPad, we want to allow proper viewport resizing
|
|
96
|
-
landscapeIos.value = window.matchMedia("(orientation: landscape)").matches;
|
|
97
|
-
document.documentElement.style.setProperty("width", "100%", "important");
|
|
98
|
-
document.body.style.setProperty("width", "100%", "important");
|
|
99
|
-
} else {
|
|
100
|
-
// For iPhone, maintain the current behavior
|
|
101
|
-
landscapeIos.value = window.matchMedia("(orientation: landscape)").matches;
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
};
|
|
105
|
-
|
|
106
|
-
// Initial orientation check
|
|
107
|
-
handleOrientationChange();
|
|
108
|
-
|
|
109
|
-
// Listen for orientation changes
|
|
110
|
-
window.matchMedia("(orientation: portrait)").addEventListener("change", handleOrientationChange);
|
|
111
|
-
window.matchMedia("(orientation: landscape)").addEventListener("change", handleOrientationChange);
|
|
91
|
+
window.matchMedia("(orientation: portrait)").addEventListener("change", (e) => {
|
|
92
|
+
if (!e.matches && isIOS() && !isIpadOS()) landscapeIos.value = true;
|
|
93
|
+
else landscapeIos.value = false;
|
|
94
|
+
});
|
|
112
95
|
|
|
113
96
|
if (!window.location.href.includes(":5173")) ui.startSpinner("Loading...");
|
|
114
97
|
|
|
115
|
-
// Ensure notch handling runs when Vue app mounts
|
|
116
|
-
onMounted(() => {
|
|
117
|
-
// Multiple aggressive attempts on Vue mount
|
|
118
|
-
handleNotch();
|
|
119
|
-
setTimeout(handleNotch, 10);
|
|
120
|
-
setTimeout(handleNotch, 50);
|
|
121
|
-
setTimeout(handleNotch, 150);
|
|
122
|
-
setTimeout(handleNotch, 300);
|
|
123
|
-
|
|
124
|
-
// Set up a recurring check for the first few seconds
|
|
125
|
-
let attempts = 0;
|
|
126
|
-
const recurringCheck = setInterval(() => {
|
|
127
|
-
if (attempts++ > 10) {
|
|
128
|
-
clearInterval(recurringCheck);
|
|
129
|
-
return;
|
|
130
|
-
}
|
|
131
|
-
handleNotch();
|
|
132
|
-
}, 200);
|
|
133
|
-
});
|
|
134
|
-
|
|
135
98
|
// close modals on esc
|
|
136
99
|
document.onkeydown = function (evt) {
|
|
137
100
|
evt = evt || window.event;
|
|
@@ -159,64 +122,13 @@ document.addEventListener("keydown", function (event) {
|
|
|
159
122
|
event.preventDefault();
|
|
160
123
|
}
|
|
161
124
|
});
|
|
162
|
-
//
|
|
163
|
-
let isIOSDevice =
|
|
164
|
-
/iPad|iPhone|iPod/.test(navigator.platform) ||
|
|
165
|
-
(navigator.maxTouchPoints && navigator.maxTouchPoints > 2 && /MacIntel/.test(navigator.platform));
|
|
166
|
-
|
|
167
|
-
if (isIOSDevice) {
|
|
168
|
-
const handleViewportResize = () => {
|
|
169
|
-
if (isIpadOS()) {
|
|
170
|
-
// For iPad, allow natural viewport behavior
|
|
171
|
-
return;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
// For iPhone, maintain viewport stability
|
|
175
|
-
const vh = window.innerHeight * 0.01;
|
|
176
|
-
document.documentElement.style.setProperty("--vh", `${vh}px`);
|
|
177
|
-
};
|
|
178
|
-
|
|
179
|
-
// Initial setup
|
|
180
|
-
handleViewportResize();
|
|
181
|
-
|
|
182
|
-
// Listen for viewport changes
|
|
183
|
-
window.addEventListener("resize", handleViewportResize);
|
|
184
|
-
if (window.visualViewport) {
|
|
185
|
-
window.visualViewport.addEventListener("resize", handleViewportResize);
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
// Precise mouse wheel control - only allow scrolling within specific scrollable elements
|
|
125
|
+
// prevent zoom
|
|
190
126
|
window.addEventListener(
|
|
191
127
|
"mousewheel",
|
|
192
128
|
function (event) {
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
event.target.tagName === "TEXTAREA" &&
|
|
196
|
-
(event.target.classList.contains("code-editor") || event.target.classList.contains("proxy-editor"));
|
|
197
|
-
|
|
198
|
-
// Check if we're in a table but only allow scrolling on actual scrollable content
|
|
199
|
-
const isInTable = event.target.closest(".table-component");
|
|
200
|
-
const isScrollableTableContent =
|
|
201
|
-
isInTable &&
|
|
202
|
-
(event.target.closest(".grid") || // Table rows
|
|
203
|
-
event.target.closest(".table-row") ||
|
|
204
|
-
(event.target.closest(".table-component") && !event.target.closest(".table-header")));
|
|
205
|
-
|
|
206
|
-
// Allow scrolling in navbar and modals
|
|
207
|
-
const isInNavbar = event.target.closest(".navbar") || event.target.closest(".mobile-menu");
|
|
208
|
-
const isInModal = event.target.closest('[role="dialog"]');
|
|
209
|
-
|
|
210
|
-
// Only allow these specific cases
|
|
211
|
-
if (isScrollableTextarea || isScrollableTableContent || isInNavbar || isInModal) {
|
|
212
|
-
// Stop propagation to prevent page scroll
|
|
213
|
-
if (isScrollableTableContent || isScrollableTextarea) {
|
|
214
|
-
event.stopPropagation();
|
|
215
|
-
}
|
|
216
|
-
return;
|
|
129
|
+
if (event.ctrlKey == true) {
|
|
130
|
+
event.preventDefault();
|
|
217
131
|
}
|
|
218
|
-
|
|
219
|
-
event.preventDefault();
|
|
220
132
|
},
|
|
221
133
|
{ passive: false }
|
|
222
134
|
);
|
|
@@ -224,349 +136,69 @@ window.addEventListener(
|
|
|
224
136
|
window.addEventListener(
|
|
225
137
|
"DOMMouseScroll",
|
|
226
138
|
function (event) {
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
event.target.tagName === "TEXTAREA" &&
|
|
230
|
-
(event.target.classList.contains("code-editor") || event.target.classList.contains("proxy-editor"));
|
|
231
|
-
|
|
232
|
-
const isInTable = event.target.closest(".table-component");
|
|
233
|
-
const isScrollableTableContent =
|
|
234
|
-
isInTable &&
|
|
235
|
-
(event.target.closest(".grid") ||
|
|
236
|
-
event.target.closest(".table-row") ||
|
|
237
|
-
(event.target.closest(".table-component") && !event.target.closest(".table-header")));
|
|
238
|
-
|
|
239
|
-
const isInNavbar = event.target.closest(".navbar") || event.target.closest(".mobile-menu");
|
|
240
|
-
const isInModal = event.target.closest('[role="dialog"]');
|
|
241
|
-
|
|
242
|
-
if (isScrollableTextarea || isScrollableTableContent || isInNavbar || isInModal) {
|
|
243
|
-
if (isScrollableTableContent || isScrollableTextarea) {
|
|
244
|
-
event.stopPropagation();
|
|
245
|
-
}
|
|
246
|
-
return;
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
event.preventDefault();
|
|
250
|
-
},
|
|
251
|
-
{ passive: false }
|
|
252
|
-
);
|
|
253
|
-
|
|
254
|
-
window.addEventListener(
|
|
255
|
-
"wheel",
|
|
256
|
-
function (event) {
|
|
257
|
-
// Use same logic as mousewheel
|
|
258
|
-
const isScrollableTextarea =
|
|
259
|
-
event.target.tagName === "TEXTAREA" &&
|
|
260
|
-
(event.target.classList.contains("code-editor") || event.target.classList.contains("proxy-editor"));
|
|
261
|
-
|
|
262
|
-
const isInTable = event.target.closest(".table-component");
|
|
263
|
-
const isScrollableTableContent =
|
|
264
|
-
isInTable &&
|
|
265
|
-
(event.target.closest(".grid") ||
|
|
266
|
-
event.target.closest(".table-row") ||
|
|
267
|
-
(event.target.closest(".table-component") && !event.target.closest(".table-header")));
|
|
268
|
-
|
|
269
|
-
const isInNavbar = event.target.closest(".navbar") || event.target.closest(".mobile-menu");
|
|
270
|
-
const isInModal = event.target.closest('[role="dialog"]');
|
|
271
|
-
|
|
272
|
-
if (isScrollableTextarea || isScrollableTableContent || isInNavbar || isInModal) {
|
|
273
|
-
if (isScrollableTableContent || isScrollableTextarea) {
|
|
274
|
-
event.stopPropagation();
|
|
275
|
-
}
|
|
276
|
-
return;
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
event.preventDefault();
|
|
280
|
-
},
|
|
281
|
-
{ passive: false }
|
|
282
|
-
);
|
|
283
|
-
|
|
284
|
-
window.addEventListener(
|
|
285
|
-
"scroll",
|
|
286
|
-
function (event) {
|
|
287
|
-
event.preventDefault();
|
|
288
|
-
},
|
|
289
|
-
{ passive: false }
|
|
290
|
-
);
|
|
291
|
-
|
|
292
|
-
// Precise scroll control - only allow scrolling within specific scrollable elements
|
|
293
|
-
window.addEventListener(
|
|
294
|
-
"touchmove",
|
|
295
|
-
function (event) {
|
|
296
|
-
// Check if we're touching a scrollable element directly
|
|
297
|
-
const isScrollableTextarea =
|
|
298
|
-
event.target.tagName === "TEXTAREA" &&
|
|
299
|
-
(event.target.classList.contains("code-editor") || event.target.classList.contains("proxy-editor"));
|
|
300
|
-
|
|
301
|
-
// Check if we're in a table but only allow scrolling on actual scrollable content
|
|
302
|
-
const isInTable = event.target.closest(".table-component");
|
|
303
|
-
const isScrollableTableContent =
|
|
304
|
-
isInTable &&
|
|
305
|
-
(event.target.closest(".grid") || // Table rows
|
|
306
|
-
event.target.closest(".table-row") ||
|
|
307
|
-
(event.target.closest(".table-component") && !event.target.closest(".table-header")));
|
|
308
|
-
|
|
309
|
-
// Allow scrolling in navbar and modals (they handle their own boundaries)
|
|
310
|
-
const isInNavbar = event.target.closest(".navbar") || event.target.closest(".mobile-menu");
|
|
311
|
-
const isInModal = event.target.closest('[role="dialog"]');
|
|
312
|
-
|
|
313
|
-
// Only allow these specific cases
|
|
314
|
-
if (isScrollableTextarea || isScrollableTableContent || isInNavbar || isInModal) {
|
|
315
|
-
// For table content, ensure we stop propagation to prevent page scroll
|
|
316
|
-
if (isScrollableTableContent || isScrollableTextarea) {
|
|
317
|
-
event.stopPropagation();
|
|
318
|
-
}
|
|
319
|
-
return;
|
|
139
|
+
if (event.ctrlKey == true) {
|
|
140
|
+
event.preventDefault();
|
|
320
141
|
}
|
|
321
|
-
|
|
322
|
-
// Prevent everything else
|
|
323
|
-
event.preventDefault();
|
|
324
142
|
},
|
|
325
143
|
{ passive: false }
|
|
326
144
|
);
|
|
327
145
|
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
function isLandscapeMode() {
|
|
336
|
-
// Check orientation first
|
|
337
|
-
let orientationLandscape = false;
|
|
338
|
-
|
|
339
|
-
if (typeof window.orientation !== "undefined") {
|
|
340
|
-
orientationLandscape = Math.abs(window.orientation) === 90;
|
|
341
|
-
} else if (screen.orientation && typeof screen.orientation.angle !== "undefined") {
|
|
342
|
-
orientationLandscape = Math.abs(screen.orientation.angle) === 90;
|
|
146
|
+
window.screen.orientation.onchange = () => {
|
|
147
|
+
if (document.body.classList.contains("overflow-hidden")) {
|
|
148
|
+
document.body.classList.remove("overflow-hidden");
|
|
149
|
+
setTimeout(() => {
|
|
150
|
+
document.body.classList.add("overflow-hidden");
|
|
151
|
+
}, 5);
|
|
343
152
|
}
|
|
153
|
+
handleNotch();
|
|
154
|
+
};
|
|
344
155
|
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
const hasStrongLandscapeRatio = window.innerWidth / window.innerHeight > 1.5;
|
|
348
|
-
|
|
349
|
-
// For iPhones, if dimensions clearly indicate landscape, trust that
|
|
350
|
-
if (isIOS() && !isIpadOS() && dimensionLandscape && hasStrongLandscapeRatio) {
|
|
351
|
-
return true;
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
// Otherwise use orientation if available, fallback to dimensions
|
|
355
|
-
return orientationLandscape || (dimensionLandscape && hasStrongLandscapeRatio);
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
function hasDeviceNotch() {
|
|
359
|
-
// Only check for notch on actual devices that might have one
|
|
360
|
-
if (!isIOS() || isIpadOS()) return false;
|
|
361
|
-
|
|
362
|
-
return (
|
|
363
|
-
CSS.supports("padding-left: env(safe-area-inset-left)") &&
|
|
364
|
-
CSS.supports("padding-right: env(safe-area-inset-right)")
|
|
365
|
-
);
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
function handleNotch(force = false) {
|
|
369
|
-
// Simple debouncing
|
|
370
|
-
if (isNotchBusy && !force) return;
|
|
371
|
-
|
|
372
|
-
try {
|
|
373
|
-
// Only for iPhone (not iPad)
|
|
374
|
-
if (!isIOS() || isIpadOS()) return;
|
|
375
|
-
|
|
376
|
-
const wrappers = document.querySelectorAll(".ios-wrapper");
|
|
377
|
-
if (wrappers.length === 0) {
|
|
378
|
-
setTimeout(() => handleNotch(force), 100);
|
|
379
|
-
return;
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
const isLandscape = isLandscapeMode();
|
|
383
|
-
const hasNotch = hasDeviceNotch();
|
|
384
|
-
|
|
385
|
-
// Get orientation
|
|
386
|
-
let orientation = window.orientation;
|
|
387
|
-
if (typeof orientation === "undefined" && screen.orientation) {
|
|
388
|
-
orientation = screen.orientation.angle;
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
// Create state signature to prevent redundant updates
|
|
392
|
-
const currentState = `${isLandscape}-${hasNotch}-${orientation}-${window.innerWidth}x${window.innerHeight}`;
|
|
393
|
-
if (lastNotchState === currentState && !force) {
|
|
394
|
-
return;
|
|
395
|
-
}
|
|
396
|
-
lastNotchState = currentState;
|
|
397
|
-
|
|
398
|
-
isNotchBusy = true;
|
|
399
|
-
|
|
400
|
-
// Debug info
|
|
401
|
-
if (window.location.href.includes("localhost") || window.location.href.includes("5173")) {
|
|
402
|
-
console.log("🔥 Notch Debug:", {
|
|
403
|
-
isLandscape,
|
|
404
|
-
hasNotch,
|
|
405
|
-
orientation,
|
|
406
|
-
dimensions: `${window.innerWidth}x${window.innerHeight}`,
|
|
407
|
-
state: currentState
|
|
408
|
-
});
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
if (hasNotch && isLandscape) {
|
|
412
|
-
// Test actual safe area values to determine notch side
|
|
413
|
-
const testDiv = document.createElement("div");
|
|
414
|
-
testDiv.style.position = "fixed";
|
|
415
|
-
testDiv.style.top = "0";
|
|
416
|
-
testDiv.style.left = "0";
|
|
417
|
-
testDiv.style.visibility = "hidden";
|
|
418
|
-
testDiv.style.paddingLeft = "env(safe-area-inset-left)";
|
|
419
|
-
testDiv.style.paddingRight = "env(safe-area-inset-right)";
|
|
420
|
-
document.body.appendChild(testDiv);
|
|
421
|
-
|
|
422
|
-
const computedStyle = getComputedStyle(testDiv);
|
|
423
|
-
const leftInset = parseFloat(computedStyle.paddingLeft) || 0;
|
|
424
|
-
const rightInset = parseFloat(computedStyle.paddingRight) || 0;
|
|
156
|
+
document.addEventListener("DOMContentLoaded", handleNotch, false);
|
|
157
|
+
window.simulateRotate = handleNotch;
|
|
425
158
|
|
|
426
|
-
|
|
159
|
+
function handleNotch() {
|
|
160
|
+
const wrappers = [...document.querySelectorAll(".ios-wrapper")];
|
|
427
161
|
|
|
428
|
-
|
|
162
|
+
// Check if the device is an iPhone with a notch
|
|
163
|
+
const hasNotch = CSS.supports("padding-left: env(safe-area-inset-left)");
|
|
429
164
|
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
165
|
+
const orientation = window.orientation; //window.screen.orientation.angle;
|
|
166
|
+
if (!isIpadOS() && isIOS()) {
|
|
167
|
+
if (hasNotch && orientation !== 0) {
|
|
168
|
+
// Landscape mode
|
|
169
|
+
if (orientation === 90) {
|
|
170
|
+
// Notch is on the left
|
|
171
|
+
wrappers.forEach((wrapper) => {
|
|
434
172
|
wrapper.style.paddingLeft = "env(safe-area-inset-left)";
|
|
435
173
|
wrapper.style.paddingRight = "0.5rem";
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
|
|
174
|
+
});
|
|
175
|
+
} else if (orientation === -90) {
|
|
176
|
+
// Notch is on the right
|
|
177
|
+
wrappers.forEach((wrapper) => {
|
|
439
178
|
wrapper.style.paddingRight = "env(safe-area-inset-right)";
|
|
440
|
-
} else {
|
|
441
|
-
// Fallback - no detectable notch
|
|
442
179
|
wrapper.style.paddingLeft = "0.5rem";
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
});
|
|
180
|
+
});
|
|
181
|
+
}
|
|
446
182
|
} else {
|
|
447
|
-
// Portrait or no notch
|
|
448
|
-
const padding =
|
|
449
|
-
window.innerWidth > 1280
|
|
450
|
-
? "2.5rem"
|
|
451
|
-
: window.innerWidth > 1030
|
|
452
|
-
? "1.5rem"
|
|
453
|
-
: window.innerWidth > 768
|
|
454
|
-
? "0.5rem"
|
|
455
|
-
: "0.5rem";
|
|
456
|
-
|
|
457
|
-
// Apply styles instantly - NO ANIMATION
|
|
458
183
|
wrappers.forEach((wrapper) => {
|
|
459
|
-
|
|
460
|
-
|
|
184
|
+
// Portrait mode or no notch
|
|
185
|
+
const amount =
|
|
186
|
+
window.innerWidth > 1280
|
|
187
|
+
? "2.5rem"
|
|
188
|
+
: window.innerWidth > 1030
|
|
189
|
+
? "1.5rem"
|
|
190
|
+
: window.innerWidth > 768
|
|
191
|
+
? "0.5rem"
|
|
192
|
+
: "0.5rem";
|
|
193
|
+
wrapper.style.paddingLeft = amount;
|
|
194
|
+
wrapper.style.paddingRight = amount;
|
|
461
195
|
});
|
|
462
196
|
}
|
|
463
|
-
|
|
464
|
-
isNotchBusy = false;
|
|
465
|
-
} catch (error) {
|
|
466
|
-
console.warn("Notch error:", error);
|
|
467
|
-
isNotchBusy = false;
|
|
468
197
|
}
|
|
469
198
|
}
|
|
470
199
|
|
|
471
|
-
function
|
|
472
|
-
|
|
473
|
-
notchTimeout = setTimeout(handleNotch, 50);
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
// Event listeners
|
|
477
|
-
window.addEventListener("orientationchange", triggerNotch);
|
|
478
|
-
window.addEventListener("resize", triggerNotch);
|
|
479
|
-
if (screen.orientation) {
|
|
480
|
-
screen.orientation.addEventListener("change", triggerNotch);
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
// Aggressive initial setup to handle all scenarios
|
|
484
|
-
function initNotchHandling() {
|
|
485
|
-
handleNotch(true); // Force first execution
|
|
486
|
-
setTimeout(() => handleNotch(true), 50);
|
|
487
|
-
setTimeout(() => handleNotch(true), 150);
|
|
488
|
-
setTimeout(() => handleNotch(true), 300);
|
|
489
|
-
setTimeout(() => handleNotch(true), 500);
|
|
490
|
-
setTimeout(() => handleNotch(true), 1000);
|
|
491
|
-
setTimeout(() => handleNotch(true), 2000); // Extra long delay for slow loads
|
|
492
|
-
}
|
|
493
|
-
|
|
494
|
-
// Multiple initialization points
|
|
495
|
-
if (document.readyState === "loading") {
|
|
496
|
-
document.addEventListener("DOMContentLoaded", initNotchHandling);
|
|
497
|
-
} else {
|
|
498
|
-
initNotchHandling();
|
|
499
|
-
}
|
|
500
|
-
|
|
501
|
-
window.addEventListener("load", initNotchHandling);
|
|
502
|
-
|
|
503
|
-
// Also run when page becomes visible (handles app switching)
|
|
504
|
-
document.addEventListener("visibilitychange", () => {
|
|
505
|
-
if (!document.hidden) {
|
|
506
|
-
setTimeout(handleNotch, 100);
|
|
507
|
-
}
|
|
508
|
-
});
|
|
509
|
-
|
|
510
|
-
// Run on focus (when returning to app)
|
|
511
|
-
window.addEventListener("focus", () => {
|
|
512
|
-
setTimeout(handleNotch, 100);
|
|
513
|
-
});
|
|
514
|
-
|
|
515
|
-
// Watch for ios-wrapper elements appearing in DOM
|
|
516
|
-
const observer = new MutationObserver((mutations) => {
|
|
517
|
-
let shouldTrigger = false;
|
|
518
|
-
|
|
519
|
-
mutations.forEach((mutation) => {
|
|
520
|
-
if (mutation.type === "childList") {
|
|
521
|
-
mutation.addedNodes.forEach((node) => {
|
|
522
|
-
if (node.nodeType === Node.ELEMENT_NODE) {
|
|
523
|
-
if (node.classList?.contains("ios-wrapper") || node.querySelector?.(".ios-wrapper")) {
|
|
524
|
-
shouldTrigger = true;
|
|
525
|
-
}
|
|
526
|
-
}
|
|
527
|
-
});
|
|
528
|
-
}
|
|
529
|
-
});
|
|
530
|
-
|
|
531
|
-
if (shouldTrigger) {
|
|
532
|
-
setTimeout(() => handleNotch(true), 10);
|
|
533
|
-
setTimeout(() => handleNotch(true), 100);
|
|
534
|
-
}
|
|
535
|
-
});
|
|
536
|
-
|
|
537
|
-
// Start observing
|
|
538
|
-
if (document.body) {
|
|
539
|
-
observer.observe(document.body, {
|
|
540
|
-
childList: true,
|
|
541
|
-
subtree: true
|
|
542
|
-
});
|
|
543
|
-
} else {
|
|
544
|
-
document.addEventListener("DOMContentLoaded", () => {
|
|
545
|
-
observer.observe(document.body, {
|
|
546
|
-
childList: true,
|
|
547
|
-
subtree: true
|
|
548
|
-
});
|
|
549
|
-
});
|
|
550
|
-
}
|
|
551
|
-
|
|
552
|
-
// Navigation handling
|
|
553
|
-
const originalPushState = history.pushState;
|
|
554
|
-
const originalReplaceState = history.replaceState;
|
|
555
|
-
|
|
556
|
-
history.pushState = function (...args) {
|
|
557
|
-
originalPushState.apply(this, args);
|
|
558
|
-
triggerNotch();
|
|
559
|
-
};
|
|
560
|
-
|
|
561
|
-
history.replaceState = function (...args) {
|
|
562
|
-
originalReplaceState.apply(this, args);
|
|
563
|
-
triggerNotch();
|
|
564
|
-
};
|
|
565
|
-
|
|
566
|
-
window.addEventListener("popstate", triggerNotch);
|
|
567
|
-
|
|
568
|
-
// Expose for manual triggering
|
|
569
|
-
window.simulateRotate = handleNotch;
|
|
200
|
+
// Call the function initially
|
|
201
|
+
window.addEventListener("orientationchange", handleNotch);
|
|
570
202
|
|
|
571
203
|
const pullStart = (e) => {
|
|
572
204
|
const { screenY } = e.targetTouches[0];
|
|
@@ -620,57 +252,33 @@ function maxPull() {
|
|
|
620
252
|
return 250;
|
|
621
253
|
}
|
|
622
254
|
}
|
|
623
|
-
// Vue router integration - aggressive triggering on route changes
|
|
624
255
|
watch(
|
|
625
256
|
() => router.currentRoute.value.name,
|
|
626
257
|
() => {
|
|
627
|
-
|
|
628
|
-
handleNotch();
|
|
629
|
-
triggerNotch();
|
|
630
|
-
|
|
631
|
-
// Staggered attempts
|
|
632
|
-
setTimeout(handleNotch, 10);
|
|
633
|
-
setTimeout(handleNotch, 50);
|
|
634
|
-
setTimeout(handleNotch, 150);
|
|
635
|
-
setTimeout(handleNotch, 300);
|
|
636
|
-
setTimeout(handleNotch, 500);
|
|
637
|
-
}
|
|
638
|
-
);
|
|
639
|
-
|
|
640
|
-
watch(
|
|
641
|
-
() => router.currentRoute.value.path,
|
|
642
|
-
() => {
|
|
643
|
-
// Immediate triggers
|
|
644
|
-
handleNotch();
|
|
645
|
-
triggerNotch();
|
|
646
|
-
|
|
647
|
-
// Staggered attempts
|
|
648
|
-
setTimeout(handleNotch, 25);
|
|
649
|
-
setTimeout(handleNotch, 100);
|
|
650
|
-
setTimeout(handleNotch, 250);
|
|
258
|
+
setTimeout(() => handleNotch(), 5);
|
|
651
259
|
}
|
|
652
260
|
);
|
|
653
261
|
const layout = computed(() => router.currentRoute.value.meta.layout);
|
|
654
262
|
</script>
|
|
655
263
|
<style lang="scss">
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
height: 100% !important;
|
|
663
|
-
touch-action: none !important;
|
|
264
|
+
.task-buttons {
|
|
265
|
+
@apply flex mx-auto gap-x-4;
|
|
266
|
+
svg {
|
|
267
|
+
width: 15px;
|
|
268
|
+
height: 15px;
|
|
269
|
+
}
|
|
664
270
|
}
|
|
665
271
|
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
width: 100% !important;
|
|
670
|
-
height: 100vh !important;
|
|
671
|
-
touch-action: none !important;
|
|
272
|
+
.hidden-scrollbars::-webkit-scrollbar
|
|
273
|
+
/* Hide scrollbar for Chrome, Safari and Opera */ {
|
|
274
|
+
display: none;
|
|
672
275
|
}
|
|
673
276
|
|
|
277
|
+
/* Hide scrollbar for IE, Edge and Firefox */
|
|
278
|
+
.hidden-scrollbars {
|
|
279
|
+
-ms-overflow-style: none; /* IE and Edge */
|
|
280
|
+
scrollbar-width: none; /* Firefox */
|
|
281
|
+
}
|
|
674
282
|
.dropdown {
|
|
675
283
|
position: relative;
|
|
676
284
|
display: inline-block;
|
|
@@ -684,19 +292,19 @@ body {
|
|
|
684
292
|
z-index: 1;
|
|
685
293
|
}
|
|
686
294
|
|
|
295
|
+
.smooth-hover {
|
|
296
|
+
@apply hover:opacity-70 active:opacity-50 duration-150;
|
|
297
|
+
}
|
|
298
|
+
|
|
687
299
|
.layout {
|
|
688
300
|
@apply flex flex-col;
|
|
689
301
|
min-height: 90vh;
|
|
690
|
-
overflow: hidden !important;
|
|
691
|
-
height: 100vh !important;
|
|
692
302
|
}
|
|
693
|
-
|
|
694
303
|
.router-wrapper {
|
|
695
304
|
@apply pt-5;
|
|
696
305
|
transition: margin 0.25s;
|
|
697
306
|
z-index: 0;
|
|
698
307
|
}
|
|
699
|
-
|
|
700
308
|
.refresh-container {
|
|
701
309
|
transition: margin 0.25s;
|
|
702
310
|
}
|
|
@@ -704,25 +312,4 @@ body {
|
|
|
704
312
|
.component-container {
|
|
705
313
|
@apply w-full mx-auto px-4 xs:px-4 md:px-2 lg:px-6 xl:px-10;
|
|
706
314
|
}
|
|
707
|
-
|
|
708
|
-
// Page navigation transitions
|
|
709
|
-
.page-transition-enter-active {
|
|
710
|
-
transition: all 0.2s cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
|
711
|
-
}
|
|
712
|
-
|
|
713
|
-
.page-transition-leave-active {
|
|
714
|
-
transition: all 0.15s cubic-bezier(0.55, 0.085, 0.68, 0.53);
|
|
715
|
-
}
|
|
716
|
-
|
|
717
|
-
.page-transition-enter-from {
|
|
718
|
-
opacity: 0;
|
|
719
|
-
transform: translateX(15px) scale(0.98);
|
|
720
|
-
filter: blur(1px);
|
|
721
|
-
}
|
|
722
|
-
|
|
723
|
-
.page-transition-leave-to {
|
|
724
|
-
opacity: 0;
|
|
725
|
-
transform: translateX(-10px) scale(1.02);
|
|
726
|
-
filter: blur(0.5px);
|
|
727
|
-
}
|
|
728
315
|
</style>
|