@necrolab/dashboard 0.5.15 → 0.5.17
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/backend/api.js +2 -3
- package/eslint.config.js +46 -0
- package/index.html +2 -1
- package/package.json +5 -2
- package/src/App.vue +70 -566
- package/src/assets/css/base/mixins.scss +72 -0
- package/src/assets/css/base/reset.scss +0 -2
- package/src/assets/css/base/scroll.scss +43 -36
- package/src/assets/css/base/typography.scss +9 -10
- package/src/assets/css/base/variables.scss +43 -0
- package/src/assets/css/components/accessibility.scss +37 -0
- package/src/assets/css/components/buttons.scss +61 -74
- package/src/assets/css/components/forms.scss +31 -32
- package/src/assets/css/components/headers.scss +13 -21
- package/src/assets/css/components/modals.scss +2 -2
- package/src/assets/css/components/search-groups.scss +28 -22
- package/src/assets/css/components/tables.scss +5 -7
- package/src/assets/css/components/toasts.scss +7 -7
- package/src/assets/css/components/utilities.scss +295 -0
- package/src/assets/css/main.scss +55 -139
- package/src/components/Auth/LoginForm.vue +7 -86
- package/src/components/Console/ConsoleToolbar.vue +123 -0
- package/src/components/Editors/Account/Account.vue +12 -12
- package/src/components/Editors/Account/AccountView.vue +38 -111
- package/src/components/Editors/Account/CreateAccount.vue +11 -61
- package/src/components/Editors/Account/{AccountCreator.vue → CreateAccountBatch.vue} +28 -59
- package/src/components/Editors/AdminFileEditor.vue +179 -0
- package/src/components/Editors/Profile/CreateProfile.vue +77 -150
- package/src/components/Editors/Profile/Profile.vue +20 -21
- package/src/components/Editors/Profile/ProfileCountryChooser.vue +16 -60
- package/src/components/Editors/Profile/ProfileView.vue +41 -116
- package/src/components/Editors/ProxyFileEditor.vue +86 -0
- package/src/components/Editors/TagLabel.vue +16 -55
- package/src/components/Editors/TagToggle.vue +20 -8
- package/src/components/Filter/Filter.vue +66 -79
- package/src/components/Filter/FilterPreview.vue +153 -135
- package/src/components/Filter/PriceSortToggle.vue +36 -43
- package/src/components/Table/Header.vue +1 -1
- package/src/components/Table/Table.vue +45 -51
- package/src/components/Tasks/CheckStock.vue +7 -16
- package/src/components/Tasks/Controls/DesktopControls.vue +15 -60
- package/src/components/Tasks/Controls/MobileControls.vue +5 -20
- package/src/components/Tasks/CreateTaskAXS.vue +20 -118
- package/src/components/Tasks/CreateTaskTM.vue +33 -189
- package/src/components/Tasks/EventDetailRow.vue +21 -0
- package/src/components/Tasks/MassEdit.vue +6 -16
- package/src/components/Tasks/QuickSettings.vue +140 -216
- package/src/components/Tasks/ScrapeVenue.vue +4 -13
- package/src/components/Tasks/Stats.vue +20 -39
- package/src/components/Tasks/Task.vue +64 -270
- package/src/components/Tasks/TaskLabel.vue +9 -3
- package/src/components/Tasks/TaskView.vue +45 -64
- package/src/components/Tasks/Utilities.vue +10 -44
- package/src/components/Tasks/ViewTask.vue +23 -107
- package/src/components/icons/Close.vue +2 -8
- package/src/components/icons/Gear.vue +8 -8
- package/src/components/icons/Hash.vue +5 -0
- package/src/components/icons/Key.vue +2 -8
- package/src/components/icons/Pencil.vue +2 -8
- package/src/components/icons/Profile.vue +2 -8
- package/src/components/icons/Sell.vue +2 -8
- package/src/components/icons/Spinner.vue +4 -7
- package/src/components/icons/Wildcard.vue +2 -8
- package/src/components/icons/index.js +3 -5
- package/src/components/ui/ActionButtonGroup.vue +113 -52
- package/src/components/ui/BalanceIndicator.vue +60 -0
- package/src/components/ui/EmptyState.vue +24 -0
- package/src/components/ui/EnableDisableToggle.vue +23 -0
- package/src/components/ui/FormField.vue +49 -49
- package/src/components/ui/IconLabel.vue +23 -0
- package/src/components/ui/InfoRow.vue +21 -54
- package/src/components/ui/Modal.vue +161 -54
- package/src/components/ui/Navbar.vue +63 -44
- package/src/components/ui/ReadonlyFieldsSection.vue +31 -0
- package/src/components/ui/ReconnectIndicator.vue +111 -124
- package/src/components/ui/SectionCard.vue +6 -14
- package/src/components/ui/Splash.vue +2 -10
- package/src/components/ui/StatusBadge.vue +26 -28
- package/src/components/ui/TaskToggle.vue +54 -0
- package/src/components/ui/controls/CountryChooser.vue +29 -66
- package/src/components/ui/controls/EyeToggle.vue +1 -1
- package/src/components/ui/controls/atomic/Checkbox.vue +40 -121
- package/src/components/ui/controls/atomic/Dropdown.vue +103 -139
- package/src/components/ui/controls/atomic/MultiDropdown.vue +72 -120
- package/src/components/ui/controls/atomic/Switch.vue +21 -84
- package/src/composables/useCodeEditor.js +117 -0
- package/src/composables/useColorMapping.js +15 -0
- package/src/composables/useCopyToClipboard.js +1 -1
- package/src/composables/useDateFormatting.js +21 -0
- package/src/composables/useDeviceDetection.js +14 -0
- package/src/composables/useDropdownPosition.js +1 -4
- package/src/composables/useDynamicTableHeight.js +31 -0
- package/src/composables/useEnableDisable.js +6 -0
- package/src/composables/useFilterCSS.js +71 -0
- package/src/composables/useFormValidation.js +92 -0
- package/src/composables/useGetAllTags.js +9 -0
- package/src/composables/useIOSViewportHandling.js +76 -0
- package/src/composables/useNotchHandling.js +306 -0
- package/src/composables/useRowSelection.js +0 -3
- package/src/composables/useTableRender.js +23 -0
- package/src/composables/useTicketPricing.js +16 -0
- package/src/composables/useWindowDimensions.js +21 -0
- package/src/composables/useZoomPrevention.js +96 -0
- package/src/constants/tableLayout.js +14 -0
- package/src/libs/Filter.js +14 -20
- package/src/libs/panzoom.js +1 -5
- package/src/libs/utils/array.js +58 -0
- package/src/{stores/utils.js → libs/utils/dataGeneration.js} +2 -250
- package/src/libs/utils/eventUrl.js +40 -0
- package/src/libs/utils/string.js +3 -0
- package/src/libs/utils/time.js +20 -0
- package/src/libs/utils/validation.js +64 -0
- package/src/main.js +0 -2
- package/src/stores/connection.js +1 -29
- package/src/stores/logger.js +6 -12
- package/src/stores/sampleData.js +1 -2
- package/src/stores/ui.js +80 -71
- package/src/utils/tableHelpers.js +1 -0
- package/src/views/Accounts.vue +19 -38
- package/src/views/Console.vue +74 -253
- package/src/views/Editor.vue +47 -1114
- package/src/views/FilterBuilder.vue +190 -461
- package/src/views/Login.vue +3 -28
- package/src/views/Profiles.vue +17 -32
- package/src/views/Tasks.vue +51 -38
- package/tailwind.config.js +82 -71
- package/workbox-config.cjs +47 -5
- package/docs/plans/2026-02-08-tailwind-consolidation.md +0 -2438
- package/exit +0 -209
- package/run +0 -177
- package/src/assets/css/base/color-fallbacks.scss +0 -10
- package/src/assets/img/background.svg.backup +0 -11
- package/src/components/icons/SquareCheck.vue +0 -18
- package/src/components/icons/SquareUncheck.vue +0 -18
- package/src/components/ui/controls/atomic/LoadingButton.vue +0 -45
- package/switch-branch.sh +0 -41
- /package/public/{reconnect-logo.png → img/reconnect-logo.png} +0 -0
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
import { ref, watch, onMounted } from 'vue';
|
|
2
|
+
import { useRouter } from 'vue-router';
|
|
3
|
+
import { useDeviceDetection } from './useDeviceDetection';
|
|
4
|
+
import { DEBUG } from '@/utils/debug';
|
|
5
|
+
|
|
6
|
+
export function useNotchHandling(logger) {
|
|
7
|
+
const { isIOS, isIpadOS } = useDeviceDetection();
|
|
8
|
+
const router = useRouter();
|
|
9
|
+
const showLandscapeLock = ref(false);
|
|
10
|
+
|
|
11
|
+
let notchTimeout;
|
|
12
|
+
let isNotchBusy = false;
|
|
13
|
+
let lastNotchState = null;
|
|
14
|
+
|
|
15
|
+
function isLandscapeMode() {
|
|
16
|
+
let orientationLandscape = false;
|
|
17
|
+
|
|
18
|
+
if (typeof window.orientation !== "undefined") {
|
|
19
|
+
orientationLandscape = Math.abs(window.orientation) === 90;
|
|
20
|
+
} else if (screen.orientation && typeof screen.orientation.angle !== "undefined") {
|
|
21
|
+
orientationLandscape = Math.abs(screen.orientation.angle) === 90;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const dimensionLandscape = window.innerWidth > window.innerHeight;
|
|
25
|
+
const hasStrongLandscapeRatio = window.innerWidth / window.innerHeight > 1.5;
|
|
26
|
+
|
|
27
|
+
if (isIOS() && !isIpadOS() && dimensionLandscape && hasStrongLandscapeRatio) {
|
|
28
|
+
return true;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return orientationLandscape || (dimensionLandscape && hasStrongLandscapeRatio);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function hasDeviceNotch() {
|
|
35
|
+
if (!isIOS() || isIpadOS()) return false;
|
|
36
|
+
|
|
37
|
+
return (
|
|
38
|
+
CSS.supports("padding-left: env(safe-area-inset-left)") &&
|
|
39
|
+
CSS.supports("padding-right: env(safe-area-inset-right)")
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function handleNotch(force = false) {
|
|
44
|
+
if (isNotchBusy && !force) return;
|
|
45
|
+
|
|
46
|
+
try {
|
|
47
|
+
if (!isIOS() || isIpadOS()) return;
|
|
48
|
+
|
|
49
|
+
const wrappers = document.querySelectorAll(".ios-wrapper");
|
|
50
|
+
if (wrappers.length === 0) {
|
|
51
|
+
setTimeout(() => handleNotch(force), 100);
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const isLandscape = isLandscapeMode();
|
|
56
|
+
const hasNotch = hasDeviceNotch();
|
|
57
|
+
|
|
58
|
+
let orientation = window.orientation;
|
|
59
|
+
if (typeof orientation === "undefined" && screen.orientation) {
|
|
60
|
+
orientation = screen.orientation.angle;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const currentState = `${isLandscape}-${hasNotch}-${orientation}-${window.innerWidth}x${window.innerHeight}`;
|
|
64
|
+
if (lastNotchState === currentState && !force) {
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
lastNotchState = currentState;
|
|
68
|
+
|
|
69
|
+
isNotchBusy = true;
|
|
70
|
+
|
|
71
|
+
if (DEBUG && logger) {
|
|
72
|
+
logger.Debug("🔥 Notch Debug:", {
|
|
73
|
+
isLandscape,
|
|
74
|
+
hasNotch,
|
|
75
|
+
orientation,
|
|
76
|
+
dimensions: `${window.innerWidth}x${window.innerHeight}`,
|
|
77
|
+
state: currentState
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (hasNotch && isLandscape) {
|
|
82
|
+
const testDiv = document.createElement("div");
|
|
83
|
+
testDiv.style.position = "fixed";
|
|
84
|
+
testDiv.style.top = "0";
|
|
85
|
+
testDiv.style.left = "0";
|
|
86
|
+
testDiv.style.visibility = "hidden";
|
|
87
|
+
testDiv.style.paddingLeft = "env(safe-area-inset-left)";
|
|
88
|
+
testDiv.style.paddingRight = "env(safe-area-inset-right)";
|
|
89
|
+
document.body.appendChild(testDiv);
|
|
90
|
+
|
|
91
|
+
const computedStyle = getComputedStyle(testDiv);
|
|
92
|
+
const leftInset = parseFloat(computedStyle.paddingLeft) || 0;
|
|
93
|
+
const rightInset = parseFloat(computedStyle.paddingRight) || 0;
|
|
94
|
+
|
|
95
|
+
document.body.removeChild(testDiv);
|
|
96
|
+
|
|
97
|
+
if (DEBUG && logger) logger.Debug("🔍 Safe area insets:", { leftInset, rightInset });
|
|
98
|
+
|
|
99
|
+
wrappers.forEach((wrapper) => {
|
|
100
|
+
if (leftInset > 0) {
|
|
101
|
+
wrapper.style.paddingLeft = "env(safe-area-inset-left)";
|
|
102
|
+
wrapper.style.paddingRight = "0.5rem";
|
|
103
|
+
} else if (rightInset > 0) {
|
|
104
|
+
wrapper.style.paddingLeft = "0.5rem";
|
|
105
|
+
wrapper.style.paddingRight = "env(safe-area-inset-right)";
|
|
106
|
+
} else {
|
|
107
|
+
wrapper.style.paddingLeft = "0.5rem";
|
|
108
|
+
wrapper.style.paddingRight = "0.5rem";
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
} else {
|
|
112
|
+
const RESPONSIVE_PADDING = {
|
|
113
|
+
XL: { minWidth: 1280, padding: "2.5rem" },
|
|
114
|
+
LG: { minWidth: 1030, padding: "1.5rem" },
|
|
115
|
+
MD: { minWidth: 768, padding: "0.5rem" },
|
|
116
|
+
DEFAULT: { padding: "0.5rem" }
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
const padding =
|
|
120
|
+
window.innerWidth > RESPONSIVE_PADDING.XL.minWidth
|
|
121
|
+
? RESPONSIVE_PADDING.XL.padding
|
|
122
|
+
: window.innerWidth > RESPONSIVE_PADDING.LG.minWidth
|
|
123
|
+
? RESPONSIVE_PADDING.LG.padding
|
|
124
|
+
: window.innerWidth > RESPONSIVE_PADDING.MD.minWidth
|
|
125
|
+
? RESPONSIVE_PADDING.MD.padding
|
|
126
|
+
: RESPONSIVE_PADDING.DEFAULT.padding;
|
|
127
|
+
|
|
128
|
+
wrappers.forEach((wrapper) => {
|
|
129
|
+
wrapper.style.paddingLeft = padding;
|
|
130
|
+
wrapper.style.paddingRight = padding;
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
isNotchBusy = false;
|
|
135
|
+
} catch (error) {
|
|
136
|
+
if (DEBUG && logger) logger.Yellow("Notch error:", error);
|
|
137
|
+
isNotchBusy = false;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function triggerNotch() {
|
|
142
|
+
clearTimeout(notchTimeout);
|
|
143
|
+
notchTimeout = setTimeout(handleNotch, 50);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function updateLandscapeLock() {
|
|
147
|
+
if (isIOS() && !isIpadOS()) {
|
|
148
|
+
showLandscapeLock.value = isLandscapeMode();
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function initNotchHandling() {
|
|
153
|
+
handleNotch(true);
|
|
154
|
+
setTimeout(() => handleNotch(true), 50);
|
|
155
|
+
setTimeout(() => handleNotch(true), 150);
|
|
156
|
+
setTimeout(() => handleNotch(true), 300);
|
|
157
|
+
setTimeout(() => handleNotch(true), 500);
|
|
158
|
+
setTimeout(() => handleNotch(true), 1000);
|
|
159
|
+
setTimeout(() => handleNotch(true), 2000);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Event listeners
|
|
163
|
+
window.addEventListener("orientationchange", () => {
|
|
164
|
+
updateLandscapeLock();
|
|
165
|
+
triggerNotch();
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
window.addEventListener("resize", () => {
|
|
169
|
+
updateLandscapeLock();
|
|
170
|
+
triggerNotch();
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
if (screen.orientation) {
|
|
174
|
+
screen.orientation.addEventListener("change", triggerNotch);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Multiple initialization points
|
|
178
|
+
if (document.readyState === "loading") {
|
|
179
|
+
document.addEventListener("DOMContentLoaded", initNotchHandling);
|
|
180
|
+
} else {
|
|
181
|
+
initNotchHandling();
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
window.addEventListener("load", initNotchHandling);
|
|
185
|
+
|
|
186
|
+
document.addEventListener("visibilitychange", () => {
|
|
187
|
+
if (!document.hidden) {
|
|
188
|
+
setTimeout(handleNotch, 100);
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
window.addEventListener("focus", () => {
|
|
193
|
+
setTimeout(handleNotch, 100);
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
// Watch for ios-wrapper elements appearing in DOM
|
|
197
|
+
const observer = new MutationObserver((mutations) => {
|
|
198
|
+
let shouldTrigger = false;
|
|
199
|
+
|
|
200
|
+
mutations.forEach((mutation) => {
|
|
201
|
+
if (mutation.type === "childList") {
|
|
202
|
+
mutation.addedNodes.forEach((node) => {
|
|
203
|
+
if (node.nodeType === Node.ELEMENT_NODE) {
|
|
204
|
+
if (node.classList?.contains("ios-wrapper") || node.querySelector?.(".ios-wrapper")) {
|
|
205
|
+
shouldTrigger = true;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
if (shouldTrigger) {
|
|
213
|
+
setTimeout(() => handleNotch(true), 10);
|
|
214
|
+
setTimeout(() => handleNotch(true), 100);
|
|
215
|
+
}
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
if (document.body) {
|
|
219
|
+
observer.observe(document.body, {
|
|
220
|
+
childList: true,
|
|
221
|
+
subtree: true
|
|
222
|
+
});
|
|
223
|
+
} else {
|
|
224
|
+
document.addEventListener("DOMContentLoaded", () => {
|
|
225
|
+
observer.observe(document.body, {
|
|
226
|
+
childList: true,
|
|
227
|
+
subtree: true
|
|
228
|
+
});
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// Navigation handling
|
|
233
|
+
const originalPushState = history.pushState;
|
|
234
|
+
const originalReplaceState = history.replaceState;
|
|
235
|
+
|
|
236
|
+
history.pushState = function (...args) {
|
|
237
|
+
originalPushState.apply(this, args);
|
|
238
|
+
triggerNotch();
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
history.replaceState = function (...args) {
|
|
242
|
+
originalReplaceState.apply(this, args);
|
|
243
|
+
triggerNotch();
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
window.addEventListener("popstate", triggerNotch);
|
|
247
|
+
|
|
248
|
+
// Expose for manual triggering
|
|
249
|
+
window.simulateRotate = handleNotch;
|
|
250
|
+
|
|
251
|
+
// Initial landscape lock check
|
|
252
|
+
updateLandscapeLock();
|
|
253
|
+
|
|
254
|
+
// Vue-specific watchers
|
|
255
|
+
onMounted(() => {
|
|
256
|
+
let attempts = 0;
|
|
257
|
+
const maxAttempts = 5;
|
|
258
|
+
const delays = [0, 50, 150, 300, 500];
|
|
259
|
+
|
|
260
|
+
const scheduleNextAttempt = (index) => {
|
|
261
|
+
if (index >= maxAttempts) return;
|
|
262
|
+
setTimeout(() => {
|
|
263
|
+
handleNotch();
|
|
264
|
+
scheduleNextAttempt(index + 1);
|
|
265
|
+
}, delays[index]);
|
|
266
|
+
};
|
|
267
|
+
|
|
268
|
+
scheduleNextAttempt(0);
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
if (router) {
|
|
272
|
+
watch(
|
|
273
|
+
() => router.currentRoute.value.name,
|
|
274
|
+
() => {
|
|
275
|
+
handleNotch();
|
|
276
|
+
triggerNotch();
|
|
277
|
+
|
|
278
|
+
setTimeout(handleNotch, 10);
|
|
279
|
+
setTimeout(handleNotch, 50);
|
|
280
|
+
setTimeout(handleNotch, 150);
|
|
281
|
+
setTimeout(handleNotch, 300);
|
|
282
|
+
setTimeout(handleNotch, 500);
|
|
283
|
+
}
|
|
284
|
+
);
|
|
285
|
+
|
|
286
|
+
watch(
|
|
287
|
+
() => router.currentRoute.value.path,
|
|
288
|
+
() => {
|
|
289
|
+
handleNotch();
|
|
290
|
+
triggerNotch();
|
|
291
|
+
|
|
292
|
+
setTimeout(handleNotch, 25);
|
|
293
|
+
setTimeout(handleNotch, 100);
|
|
294
|
+
setTimeout(handleNotch, 250);
|
|
295
|
+
}
|
|
296
|
+
);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
return {
|
|
300
|
+
showLandscapeLock,
|
|
301
|
+
handleNotch,
|
|
302
|
+
triggerNotch,
|
|
303
|
+
updateLandscapeLock,
|
|
304
|
+
isLandscapeMode
|
|
305
|
+
};
|
|
306
|
+
}
|
|
@@ -12,7 +12,6 @@ export function useRowSelection(toggleCallback) {
|
|
|
12
12
|
const DOUBLE_TAP_DELAY = 300
|
|
13
13
|
|
|
14
14
|
const handleDoubleClick = (event) => {
|
|
15
|
-
// Don't trigger on button or checkbox clicks
|
|
16
15
|
if (event.target.closest('button') || event.target.closest('.checkbox')) {
|
|
17
16
|
return
|
|
18
17
|
}
|
|
@@ -24,7 +23,6 @@ export function useRowSelection(toggleCallback) {
|
|
|
24
23
|
const tapGap = currentTime - lastTapTime
|
|
25
24
|
|
|
26
25
|
if (tapGap < DOUBLE_TAP_DELAY && tapGap > 0) {
|
|
27
|
-
// Don't trigger on button or checkbox taps
|
|
28
26
|
if (!event.target.closest('button') && !event.target.closest('.checkbox')) {
|
|
29
27
|
event.preventDefault()
|
|
30
28
|
toggleCallback()
|
|
@@ -34,7 +32,6 @@ export function useRowSelection(toggleCallback) {
|
|
|
34
32
|
}
|
|
35
33
|
|
|
36
34
|
const handleTouchEnd = (event) => {
|
|
37
|
-
// Prevent default touch behavior on buttons/checkboxes
|
|
38
35
|
if (event.target.closest('button') || event.target.closest('.checkbox')) {
|
|
39
36
|
return
|
|
40
37
|
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { ref, computed } from 'vue';
|
|
2
|
+
|
|
3
|
+
export function useTableRender(items) {
|
|
4
|
+
const i = ref({});
|
|
5
|
+
|
|
6
|
+
const toRender = computed(() => {
|
|
7
|
+
let c = 0;
|
|
8
|
+
const rendered = items.value.map((t) => ({ ...t, index: c++ }));
|
|
9
|
+
|
|
10
|
+
rendered.forEach((t) => {
|
|
11
|
+
if (t.id && !(t.id in i.value)) {
|
|
12
|
+
i.value[t.id] = 0;
|
|
13
|
+
}
|
|
14
|
+
if (!(t.index in i.value)) {
|
|
15
|
+
i.value[t.index] = 0;
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
return rendered;
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
return { toRender };
|
|
23
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export function useTicketPricing() {
|
|
2
|
+
const isTotalPrice = (line, index, lines) => {
|
|
3
|
+
const trimmed = line.trim();
|
|
4
|
+
if (!trimmed) return false;
|
|
5
|
+
|
|
6
|
+
const nonEmptyLines = lines.filter(l => l.trim());
|
|
7
|
+
const isLastLine = index === lines.lastIndexOf(nonEmptyLines[nonEmptyLines.length - 1]);
|
|
8
|
+
|
|
9
|
+
if (!isLastLine) return false;
|
|
10
|
+
|
|
11
|
+
const totalPricePattern = /^([$€£¥₹₽¢]|[A-Z]{3})\s*[\d,]+\.?\d*$/;
|
|
12
|
+
return totalPricePattern.test(trimmed) && !trimmed.includes('(') && !trimmed.includes(')');
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
return { isTotalPrice };
|
|
16
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { ref, onMounted, onUnmounted } from "vue";
|
|
2
|
+
|
|
3
|
+
export function useWindowDimensions() {
|
|
4
|
+
const windowHeight = ref(window.innerHeight);
|
|
5
|
+
const windowWidth = ref(window.innerWidth);
|
|
6
|
+
|
|
7
|
+
const updateDimensions = () => {
|
|
8
|
+
windowHeight.value = window.innerHeight;
|
|
9
|
+
windowWidth.value = window.innerWidth;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
onMounted(() => {
|
|
13
|
+
window.addEventListener("resize", updateDimensions);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
onUnmounted(() => {
|
|
17
|
+
window.removeEventListener("resize", updateDimensions);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
return { windowHeight, windowWidth };
|
|
21
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
export function useZoomPrevention() {
|
|
2
|
+
const KEY_CODES = {
|
|
3
|
+
ESCAPE: 27,
|
|
4
|
+
EQUAL: 61,
|
|
5
|
+
NUMPAD_PLUS: 107,
|
|
6
|
+
FIREFOX_MINUS: 173,
|
|
7
|
+
NUMPAD_MINUS: 109,
|
|
8
|
+
CHROME_EQUAL: 187,
|
|
9
|
+
MINUS: 189
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
// Prevent pinch-to-zoom gestures
|
|
13
|
+
document.addEventListener('touchstart', (e) => {
|
|
14
|
+
if (e.touches.length > 1) {
|
|
15
|
+
e.preventDefault();
|
|
16
|
+
}
|
|
17
|
+
}, { passive: false });
|
|
18
|
+
|
|
19
|
+
document.addEventListener('touchmove', (e) => {
|
|
20
|
+
if (e.touches.length > 1) {
|
|
21
|
+
e.preventDefault();
|
|
22
|
+
}
|
|
23
|
+
}, { passive: false });
|
|
24
|
+
|
|
25
|
+
document.addEventListener('gesturestart', (e) => {
|
|
26
|
+
e.preventDefault();
|
|
27
|
+
}, { passive: false });
|
|
28
|
+
|
|
29
|
+
document.addEventListener('gesturechange', (e) => {
|
|
30
|
+
e.preventDefault();
|
|
31
|
+
}, { passive: false });
|
|
32
|
+
|
|
33
|
+
document.addEventListener('gestureend', (e) => {
|
|
34
|
+
e.preventDefault();
|
|
35
|
+
}, { passive: false });
|
|
36
|
+
|
|
37
|
+
// Prevent double-tap zoom
|
|
38
|
+
let lastTouchEnd = 0;
|
|
39
|
+
document.addEventListener('touchend', (e) => {
|
|
40
|
+
const now = Date.now();
|
|
41
|
+
if (now - lastTouchEnd <= 300) {
|
|
42
|
+
e.preventDefault();
|
|
43
|
+
}
|
|
44
|
+
lastTouchEnd = now;
|
|
45
|
+
}, { passive: false });
|
|
46
|
+
|
|
47
|
+
// Prevent ALL keyboard zoom combinations
|
|
48
|
+
document.addEventListener("keydown", function (event) {
|
|
49
|
+
if (
|
|
50
|
+
(event.ctrlKey || event.metaKey) &&
|
|
51
|
+
(event.which === KEY_CODES.EQUAL ||
|
|
52
|
+
event.which === KEY_CODES.NUMPAD_PLUS ||
|
|
53
|
+
event.which === KEY_CODES.FIREFOX_MINUS ||
|
|
54
|
+
event.which === KEY_CODES.NUMPAD_MINUS ||
|
|
55
|
+
event.which === KEY_CODES.CHROME_EQUAL ||
|
|
56
|
+
event.which === KEY_CODES.MINUS ||
|
|
57
|
+
event.keyCode === KEY_CODES.EQUAL ||
|
|
58
|
+
event.keyCode === KEY_CODES.NUMPAD_PLUS ||
|
|
59
|
+
event.keyCode === KEY_CODES.FIREFOX_MINUS ||
|
|
60
|
+
event.keyCode === KEY_CODES.NUMPAD_MINUS ||
|
|
61
|
+
event.keyCode === KEY_CODES.CHROME_EQUAL ||
|
|
62
|
+
event.keyCode === KEY_CODES.MINUS ||
|
|
63
|
+
event.key === '+' ||
|
|
64
|
+
event.key === '-' ||
|
|
65
|
+
event.key === '=' ||
|
|
66
|
+
event.key === '0')
|
|
67
|
+
) {
|
|
68
|
+
event.preventDefault();
|
|
69
|
+
}
|
|
70
|
+
}, { passive: false });
|
|
71
|
+
|
|
72
|
+
// Prevent Ctrl/Cmd + Mouse wheel zoom (desktop)
|
|
73
|
+
document.addEventListener("wheel", function (event) {
|
|
74
|
+
if (event.ctrlKey || event.metaKey) {
|
|
75
|
+
event.preventDefault();
|
|
76
|
+
}
|
|
77
|
+
}, { passive: false });
|
|
78
|
+
|
|
79
|
+
// Also block mousewheel for older browsers
|
|
80
|
+
document.addEventListener("mousewheel", function (event) {
|
|
81
|
+
if (event.ctrlKey || event.metaKey) {
|
|
82
|
+
event.preventDefault();
|
|
83
|
+
}
|
|
84
|
+
}, { passive: false });
|
|
85
|
+
|
|
86
|
+
// Block DOMMouseScroll for Firefox
|
|
87
|
+
document.addEventListener("DOMMouseScroll", function (event) {
|
|
88
|
+
if (event.ctrlKey || event.metaKey) {
|
|
89
|
+
event.preventDefault();
|
|
90
|
+
}
|
|
91
|
+
}, { passive: false });
|
|
92
|
+
|
|
93
|
+
return {
|
|
94
|
+
KEY_CODES
|
|
95
|
+
};
|
|
96
|
+
}
|
package/src/libs/Filter.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
const log = (...args) =>
|
|
1
|
+
import { DEBUG } from "@/utils/debug";
|
|
2
|
+
const log = (...args) => DEBUG && console.log("[filter]", ...args);
|
|
3
3
|
|
|
4
4
|
const colors = {
|
|
5
5
|
HIGHLIGHT: "#d3f8e2",
|
|
@@ -30,27 +30,23 @@ const isWheelchair = (p) => {
|
|
|
30
30
|
};
|
|
31
31
|
|
|
32
32
|
const sortAlphaNum = (a, b) => {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
33
|
+
const reA = /[^a-zA-Z]/g;
|
|
34
|
+
const reN = /[^0-9]/g;
|
|
35
|
+
const AInt = parseInt(a, 10);
|
|
36
|
+
const BInt = parseInt(b, 10);
|
|
37
37
|
|
|
38
38
|
if (isNaN(AInt) && isNaN(BInt)) {
|
|
39
|
-
|
|
40
|
-
|
|
39
|
+
const aA = a.replace(reA, "");
|
|
40
|
+
const bA = b.replace(reA, "");
|
|
41
41
|
if (aA === bA) {
|
|
42
|
-
|
|
43
|
-
|
|
42
|
+
const aN = parseInt(a.replace(reN, ""), 10);
|
|
43
|
+
const bN = parseInt(b.replace(reN, ""), 10);
|
|
44
44
|
return aN === bN ? 0 : aN > bN ? 1 : -1;
|
|
45
45
|
} else {
|
|
46
46
|
return aA > bA ? 1 : -1;
|
|
47
47
|
}
|
|
48
|
-
} else if (isNaN(AInt)) {
|
|
49
|
-
//A is not an Int
|
|
50
|
-
return 1; //to make alphanumeric sort first return -1 here
|
|
51
48
|
} else if (isNaN(BInt)) {
|
|
52
|
-
|
|
53
|
-
return -1; //to make alphanumeric sort first return 1 here
|
|
49
|
+
return -1;
|
|
54
50
|
} else {
|
|
55
51
|
return AInt > BInt ? 1 : -1;
|
|
56
52
|
}
|
|
@@ -313,7 +309,7 @@ export default class FilterBuilder {
|
|
|
313
309
|
});
|
|
314
310
|
|
|
315
311
|
if (existingFilter) {
|
|
316
|
-
|
|
312
|
+
if (DEBUG) log("Filter already exists:", existingFilter);
|
|
317
313
|
return; // Don't add the filter if it already exists
|
|
318
314
|
}
|
|
319
315
|
|
|
@@ -385,7 +381,7 @@ export default class FilterBuilder {
|
|
|
385
381
|
} else {
|
|
386
382
|
color = this.expandedFilter === filter.id ? colors.SELECTED_EXPANDED : colors.SELECTED;
|
|
387
383
|
}
|
|
388
|
-
|
|
384
|
+
|
|
389
385
|
switch (type) {
|
|
390
386
|
case this.filterTypes.NORMAL:
|
|
391
387
|
// If it has no 'rows' property
|
|
@@ -416,7 +412,6 @@ export default class FilterBuilder {
|
|
|
416
412
|
break;
|
|
417
413
|
|
|
418
414
|
case this.filterTypes.CATCH_ALL_FLOOR:
|
|
419
|
-
// this.cssClasses += floors.map((f) => `path[name="${f}"] {fill: ${color} !important;}`).join("\n") + "\n";
|
|
420
415
|
break;
|
|
421
416
|
|
|
422
417
|
case this.filterTypes.INVALID:
|
|
@@ -432,7 +427,7 @@ export default class FilterBuilder {
|
|
|
432
427
|
const color = colors.UNSELECTABLE;
|
|
433
428
|
this.cssClasses += `.svg-wrapper path[section="${section}"][row="${row}"] {stroke: ${color} !important;}\n`;
|
|
434
429
|
});
|
|
435
|
-
|
|
430
|
+
|
|
436
431
|
log("Generated CSS:", this.cssClasses);
|
|
437
432
|
}
|
|
438
433
|
|
|
@@ -533,7 +528,6 @@ export default class FilterBuilder {
|
|
|
533
528
|
|
|
534
529
|
deleteFilterById(id) {
|
|
535
530
|
this.filters = this.filters.filter((f) => f.id !== id);
|
|
536
|
-
// this.updateHooks = this.updateHooks.filter((i) => id !== i);
|
|
537
531
|
if (this.expandedFilter === id) this.expandedFilter = "";
|
|
538
532
|
this.updateCss();
|
|
539
533
|
this.updateHooks.forEach((fn) => fn());
|
package/src/libs/panzoom.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
export default (function (f) {
|
|
2
2
|
const panzoom = f();
|
|
3
3
|
window.panzoom = panzoom;
|
|
4
|
-
// module.exports = panzoom;
|
|
5
4
|
return panzoom;
|
|
6
5
|
})(function () {
|
|
7
6
|
var define, module, exports;
|
|
@@ -718,7 +717,6 @@ export default (function (f) {
|
|
|
718
717
|
failTransformOrigin();
|
|
719
718
|
}
|
|
720
719
|
function failTransformOrigin(options) {
|
|
721
|
-
console.error(options);
|
|
722
720
|
throw new Error(
|
|
723
721
|
[
|
|
724
722
|
"Cannot parse transform origin.",
|
|
@@ -783,7 +781,6 @@ export default (function (f) {
|
|
|
783
781
|
setTimeout(tryAttach, 100);
|
|
784
782
|
return;
|
|
785
783
|
}
|
|
786
|
-
console.error("Cannot find the panzoom element", globalName);
|
|
787
784
|
return;
|
|
788
785
|
}
|
|
789
786
|
var options = collectOptions(panzoomScript);
|
|
@@ -1107,8 +1104,7 @@ export default (function (f) {
|
|
|
1107
1104
|
var easing = typeof options.easing === "function" ? options.easing : animations[options.easing];
|
|
1108
1105
|
if (!easing) {
|
|
1109
1106
|
if (options.easing) {
|
|
1110
|
-
|
|
1111
|
-
}
|
|
1107
|
+
}
|
|
1112
1108
|
easing = animations.ease;
|
|
1113
1109
|
}
|
|
1114
1110
|
var step = typeof options.step === "function" ? options.step : noop;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
const removeDuplicates = (arr) => [...new Set(arr)];
|
|
2
|
+
|
|
3
|
+
function betterSort(a, b) {
|
|
4
|
+
if (a === undefined || a === null) return b === undefined || b === null ? 0 : -1;
|
|
5
|
+
if (b === undefined || b === null) return 1;
|
|
6
|
+
|
|
7
|
+
const aStr = String(a);
|
|
8
|
+
const bStr = String(b);
|
|
9
|
+
|
|
10
|
+
if (!isNaN(aStr) && !isNaN(bStr)) {
|
|
11
|
+
return Number(aStr) - Number(bStr);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const aParts = aStr.split(/(\d+)/).filter(Boolean);
|
|
15
|
+
const bParts = bStr.split(/(\d+)/).filter(Boolean);
|
|
16
|
+
|
|
17
|
+
const len = Math.min(aParts.length, bParts.length);
|
|
18
|
+
for (let i = 0; i < len; i++) {
|
|
19
|
+
if (!isNaN(aParts[i]) && !isNaN(bParts[i])) {
|
|
20
|
+
const numA = parseInt(aParts[i], 10);
|
|
21
|
+
const numB = parseInt(bParts[i], 10);
|
|
22
|
+
if (numA !== numB) return numA - numB;
|
|
23
|
+
} else {
|
|
24
|
+
const cmp = aParts[i].localeCompare(bParts[i]);
|
|
25
|
+
if (cmp !== 0) return cmp;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return aParts.length - bParts.length;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function sortTaskIds(a, b) {
|
|
33
|
+
const parseId = (id) => {
|
|
34
|
+
if (!id) return { prefix: "", num: -1 };
|
|
35
|
+
|
|
36
|
+
if (/^\d+$/.test(id)) {
|
|
37
|
+
return { prefix: "", num: parseInt(id, 10) };
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const match = id.match(/^([A-Za-z-]+)(\d+)$/);
|
|
41
|
+
if (match) {
|
|
42
|
+
return { prefix: match[1], num: parseInt(match[2], 10) };
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return { prefix: id, num: -1 };
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const aInfo = parseId(a);
|
|
49
|
+
const bInfo = parseId(b);
|
|
50
|
+
|
|
51
|
+
if (aInfo.prefix !== bInfo.prefix) {
|
|
52
|
+
return aInfo.prefix.localeCompare(bInfo.prefix);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return aInfo.num - bInfo.num;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export { removeDuplicates, betterSort, sortTaskIds };
|