underpost 2.8.84 → 2.8.86
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/.env.development +1 -0
- package/.env.production +1 -0
- package/.env.test +1 -0
- package/.github/workflows/{ghpkg.yml → ghpkg.ci.yml} +1 -1
- package/.github/workflows/{npmpkg.yml → npmpkg.ci.yml} +1 -1
- package/.github/workflows/{publish.yml → publish.ci.yml} +1 -1
- package/.github/workflows/{pwa-microservices-template.page.yml → pwa-microservices-template-page.cd.yml} +2 -2
- package/.github/workflows/{pwa-microservices-template.test.yml → pwa-microservices-template-test.ci.yml} +1 -1
- package/.github/workflows/release.cd.yml +37 -0
- package/.vscode/settings.json +0 -1
- package/README.md +16 -10
- package/bin/build.js +15 -5
- package/bin/cyberia0.js +78 -0
- package/bin/db.js +1 -3
- package/bin/deploy.js +29 -431
- package/bin/file.js +26 -9
- package/cli.md +102 -61
- package/conf.js +1 -1
- package/manifests/deployment/{dd-template-development → dd-default-development}/deployment.yaml +16 -16
- package/manifests/deployment/{dd-template-development → dd-default-development}/proxy.yaml +3 -3
- package/manifests/grafana/deployment.yaml +57 -0
- package/manifests/grafana/kustomization.yaml +7 -0
- package/manifests/grafana/pvc.yaml +12 -0
- package/manifests/grafana/service.yaml +14 -0
- package/manifests/maas/gpu-diag.sh +1 -1
- package/manifests/maas/ssh-cluster-info.sh +14 -0
- package/manifests/prometheus/deployment.yaml +82 -0
- package/package.json +4 -7
- package/src/api/user/user.router.js +24 -1
- package/src/api/user/user.service.js +9 -38
- package/src/cli/cluster.js +83 -29
- package/src/cli/cron.js +12 -45
- package/src/cli/db.js +149 -0
- package/src/cli/deploy.js +40 -81
- package/src/cli/index.js +29 -6
- package/src/cli/monitor.js +9 -16
- package/src/cli/repository.js +12 -5
- package/src/cli/run.js +175 -7
- package/src/cli/ssh.js +32 -0
- package/src/client/Default.index.js +7 -5
- package/src/client/components/core/Account.js +7 -3
- package/src/client/components/core/Chat.js +1 -1
- package/src/client/components/core/CommonJs.js +24 -22
- package/src/client/components/core/Content.js +12 -12
- package/src/client/components/core/Css.js +262 -18
- package/src/client/components/core/CssCore.js +8 -8
- package/src/client/components/core/Docs.js +14 -61
- package/src/client/components/core/DropDown.js +137 -82
- package/src/client/components/core/EventsUI.js +92 -5
- package/src/client/components/core/Input.js +6 -1
- package/src/client/components/core/LoadingAnimation.js +8 -15
- package/src/client/components/core/LogIn.js +3 -0
- package/src/client/components/core/LogOut.js +1 -1
- package/src/client/components/core/Modal.js +601 -137
- package/src/client/components/core/NotificationManager.js +2 -2
- package/src/client/components/core/ObjectLayerEngine.js +638 -0
- package/src/client/components/core/Panel.js +158 -34
- package/src/client/components/core/PanelForm.js +12 -3
- package/src/client/components/core/Recover.js +6 -3
- package/src/client/components/core/Router.js +77 -17
- package/src/client/components/core/Scroll.js +65 -120
- package/src/client/components/core/SignUp.js +1 -0
- package/src/client/components/core/SocketIo.js +3 -3
- package/src/client/components/core/Translate.js +6 -2
- package/src/client/components/core/VanillaJs.js +48 -5
- package/src/client/components/core/Worker.js +3 -1
- package/src/client/components/default/CssDefault.js +17 -3
- package/src/client/components/default/MenuDefault.js +266 -47
- package/src/client/components/default/RoutesDefault.js +8 -14
- package/src/client/public/default/android-chrome-144x144.png +0 -0
- package/src/client/public/default/android-chrome-192x192.png +0 -0
- package/src/client/public/default/android-chrome-256x256.png +0 -0
- package/src/client/public/default/android-chrome-36x36.png +0 -0
- package/src/client/public/default/android-chrome-48x48.png +0 -0
- package/src/client/public/default/android-chrome-72x72.png +0 -0
- package/src/client/public/default/android-chrome-96x96.png +0 -0
- package/src/client/public/default/apple-touch-icon-114x114-precomposed.png +0 -0
- package/src/client/public/default/apple-touch-icon-114x114.png +0 -0
- package/src/client/public/default/apple-touch-icon-120x120-precomposed.png +0 -0
- package/src/client/public/default/apple-touch-icon-120x120.png +0 -0
- package/src/client/public/default/apple-touch-icon-144x144-precomposed.png +0 -0
- package/src/client/public/default/apple-touch-icon-144x144.png +0 -0
- package/src/client/public/default/apple-touch-icon-152x152-precomposed.png +0 -0
- package/src/client/public/default/apple-touch-icon-152x152.png +0 -0
- package/src/client/public/default/apple-touch-icon-180x180-precomposed.png +0 -0
- package/src/client/public/default/apple-touch-icon-180x180.png +0 -0
- package/src/client/public/default/apple-touch-icon-57x57-precomposed.png +0 -0
- package/src/client/public/default/apple-touch-icon-57x57.png +0 -0
- package/src/client/public/default/apple-touch-icon-60x60-precomposed.png +0 -0
- package/src/client/public/default/apple-touch-icon-60x60.png +0 -0
- package/src/client/public/default/apple-touch-icon-72x72-precomposed.png +0 -0
- package/src/client/public/default/apple-touch-icon-72x72.png +0 -0
- package/src/client/public/default/apple-touch-icon-76x76-precomposed.png +0 -0
- package/src/client/public/default/apple-touch-icon-76x76.png +0 -0
- package/src/client/public/default/apple-touch-icon-precomposed.png +0 -0
- package/src/client/public/default/apple-touch-icon.png +0 -0
- package/src/client/public/default/assets/background/dark.jpg +0 -0
- package/src/client/public/default/assets/background/dark.svg +557 -0
- package/src/client/public/default/assets/logo/base-icon.png +0 -0
- package/src/client/public/default/assets/logo/underpost.gif +0 -0
- package/src/client/public/default/assets/mailer/api-user-check.png +0 -0
- package/src/client/public/default/assets/mailer/api-user-invalid-token.png +0 -0
- package/src/client/public/default/assets/mailer/api-user-recover.png +0 -0
- package/src/client/public/default/favicon-16x16.png +0 -0
- package/src/client/public/default/favicon-32x32.png +0 -0
- package/src/client/public/default/favicon.ico +0 -0
- package/src/client/public/default/mstile-144x144.png +0 -0
- package/src/client/public/default/mstile-150x150.png +0 -0
- package/src/client/public/default/mstile-310x150.png +0 -0
- package/src/client/public/default/mstile-310x310.png +0 -0
- package/src/client/public/default/mstile-70x70.png +0 -0
- package/src/client/public/default/safari-pinned-tab.svg +24 -0
- package/src/client/ssr/body/DefaultSplashScreen.js +2 -2
- package/src/index.js +9 -1
- package/src/mailer/MailerProvider.js +37 -0
- package/src/monitor.js +24 -0
- package/src/runtime/lampp/Dockerfile +30 -39
- package/src/runtime/lampp/Lampp.js +11 -2
- package/src/server/client-build-docs.js +205 -0
- package/src/server/client-build-live.js +1 -1
- package/src/server/client-build.js +16 -166
- package/src/server/client-dev-server.js +1 -1
- package/src/server/conf.js +14 -277
- package/src/server/proxy.js +1 -2
- package/src/server/start.js +3 -3
- package/src/server/valkey.js +102 -41
- package/docker-compose.yml +0 -67
- package/prometheus.yml +0 -36
|
@@ -24,11 +24,11 @@ import {
|
|
|
24
24
|
renderStatus,
|
|
25
25
|
renderCssAttr,
|
|
26
26
|
} from './Css.js';
|
|
27
|
-
import { setDocTitle } from './Router.js';
|
|
27
|
+
import { setDocTitle, closeModalRouteChangeEvent, handleModalViewRoute } from './Router.js';
|
|
28
28
|
import { NotificationManager } from './NotificationManager.js';
|
|
29
29
|
import { EventsUI } from './EventsUI.js';
|
|
30
30
|
import { Translate } from './Translate.js';
|
|
31
|
-
import { Input } from './Input.js';
|
|
31
|
+
import { Input, isTextInputFocused } from './Input.js';
|
|
32
32
|
import { Validator } from './Validator.js';
|
|
33
33
|
import { DropDown } from './DropDown.js';
|
|
34
34
|
import { Keyboard } from './Keyboard.js';
|
|
@@ -39,6 +39,7 @@ const logger = loggerFactory(import.meta);
|
|
|
39
39
|
|
|
40
40
|
const Modal = {
|
|
41
41
|
Data: {},
|
|
42
|
+
|
|
42
43
|
Render: async function (
|
|
43
44
|
options = {
|
|
44
45
|
id: '',
|
|
@@ -86,14 +87,14 @@ const Modal = {
|
|
|
86
87
|
onBarUiOpen: {},
|
|
87
88
|
onBarUiClose: {},
|
|
88
89
|
onHome: {},
|
|
90
|
+
homeModals: options.homeModals ? options.homeModals : [],
|
|
89
91
|
query: options.query ? `${window.location.search}` : undefined,
|
|
90
92
|
};
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
top = `${
|
|
94
|
-
left = `${
|
|
95
|
-
}
|
|
96
|
-
if (idModal !== 'main-body') setCenterRestore();
|
|
93
|
+
|
|
94
|
+
if (idModal !== 'main-body' && options.mode !== 'view') {
|
|
95
|
+
top = `${window.innerHeight / 2 - height / 2}px`;
|
|
96
|
+
left = `${window.innerWidth / 2 - width / 2}px`;
|
|
97
|
+
}
|
|
97
98
|
if (options && 'mode' in options) {
|
|
98
99
|
this.Data[idModal][options.mode] = {};
|
|
99
100
|
switch (options.mode) {
|
|
@@ -104,6 +105,7 @@ const Modal = {
|
|
|
104
105
|
options.style = {
|
|
105
106
|
...options.style,
|
|
106
107
|
'min-width': `${minWidth}px`,
|
|
108
|
+
width: '100%',
|
|
107
109
|
};
|
|
108
110
|
|
|
109
111
|
if (this.mobileModal()) {
|
|
@@ -124,25 +126,84 @@ const Modal = {
|
|
|
124
126
|
};
|
|
125
127
|
Responsive.Event[`view-${idModal}`]();
|
|
126
128
|
|
|
127
|
-
//
|
|
128
|
-
if (options.route)
|
|
129
|
-
(
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
if (path !== newPath) {
|
|
135
|
-
// console.warn('SET MODAL URI', newPath);
|
|
136
|
-
setPath(`${newPath}`); // ${location.search}
|
|
137
|
-
setDocTitle({ ...options.RouterInstance, route: options.route });
|
|
138
|
-
}
|
|
139
|
-
})();
|
|
129
|
+
// Handle view mode modal route
|
|
130
|
+
if (options.route) {
|
|
131
|
+
handleModalViewRoute({
|
|
132
|
+
route: options.route,
|
|
133
|
+
RouterInstance: options.RouterInstance,
|
|
134
|
+
});
|
|
135
|
+
}
|
|
140
136
|
|
|
141
137
|
break;
|
|
142
138
|
case 'slide-menu':
|
|
143
139
|
case 'slide-menu-right':
|
|
144
140
|
case 'slide-menu-left':
|
|
145
141
|
(async () => {
|
|
142
|
+
if (!options.slideMenuTopBarBannerFix) {
|
|
143
|
+
options.slideMenuTopBarBannerFix = async () => {
|
|
144
|
+
let style = html``;
|
|
145
|
+
if (options.barMode === 'top-bottom-bar') {
|
|
146
|
+
style = html`<style>
|
|
147
|
+
.default-slide-menu-top-bar-fix-logo-container {
|
|
148
|
+
width: 50px;
|
|
149
|
+
height: 50px;
|
|
150
|
+
}
|
|
151
|
+
.default-slide-menu-top-bar-fix-logo {
|
|
152
|
+
width: 40px;
|
|
153
|
+
height: 40px;
|
|
154
|
+
padding: 5px;
|
|
155
|
+
}
|
|
156
|
+
.default-slide-menu-top-bar-fix-title-container-text {
|
|
157
|
+
font-size: 26px;
|
|
158
|
+
top: 8px;
|
|
159
|
+
color: ${darkTheme ? '#ffffff' : '#000000'};
|
|
160
|
+
}
|
|
161
|
+
</style>`;
|
|
162
|
+
} else {
|
|
163
|
+
style = html`<style>
|
|
164
|
+
.default-slide-menu-top-bar-fix-logo-container {
|
|
165
|
+
width: 100px;
|
|
166
|
+
height: 100px;
|
|
167
|
+
}
|
|
168
|
+
.default-slide-menu-top-bar-fix-logo {
|
|
169
|
+
width: 50px;
|
|
170
|
+
height: 50px;
|
|
171
|
+
padding: 24px;
|
|
172
|
+
}
|
|
173
|
+
.default-slide-menu-top-bar-fix-title-container-text {
|
|
174
|
+
font-size: 30px;
|
|
175
|
+
top: 30px;
|
|
176
|
+
color: ${darkTheme ? '#ffffff' : '#000000'};
|
|
177
|
+
}
|
|
178
|
+
</style>`;
|
|
179
|
+
}
|
|
180
|
+
setTimeout(() => {
|
|
181
|
+
if (s(`.top-bar-app-icon`) && s(`.top-bar-app-icon`).src) {
|
|
182
|
+
s(`.default-slide-menu-top-bar-fix-logo`).src = s(`.top-bar-app-icon`).src;
|
|
183
|
+
if (s(`.top-bar-app-icon`).classList.contains('negative-color'))
|
|
184
|
+
s(`.default-slide-menu-top-bar-fix-logo`).classList.add('negative-color');
|
|
185
|
+
htmls(
|
|
186
|
+
`.default-slide-menu-top-bar-fix-title-container`,
|
|
187
|
+
html`
|
|
188
|
+
<div class="inl default-slide-menu-top-bar-fix-title-container-text">
|
|
189
|
+
${options.RouterInstance.BannerAppTemplate}
|
|
190
|
+
</div>
|
|
191
|
+
`,
|
|
192
|
+
);
|
|
193
|
+
} else
|
|
194
|
+
htmls(
|
|
195
|
+
`.default-slide-menu-top-bar-fix-logo-container`,
|
|
196
|
+
html`<div class="abs center">${s(`.action-btn-app-icon-render`).innerHTML}</div>`,
|
|
197
|
+
);
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
return html`${style}
|
|
201
|
+
<div class="in fll default-slide-menu-top-bar-fix-logo-container">
|
|
202
|
+
<img class="default-slide-menu-top-bar-fix-logo in fll" />
|
|
203
|
+
</div>
|
|
204
|
+
<div class="in fll default-slide-menu-top-bar-fix-title-container"></div>`;
|
|
205
|
+
};
|
|
206
|
+
}
|
|
146
207
|
const { barConfig } = options;
|
|
147
208
|
options.style = {
|
|
148
209
|
position: 'absolute',
|
|
@@ -247,8 +308,17 @@ const Modal = {
|
|
|
247
308
|
style="top: ${options.heightTopBar + 50}px; z-index: 9; ${true ||
|
|
248
309
|
(options.mode && options.mode.match('right'))
|
|
249
310
|
? 'right'
|
|
250
|
-
: 'left'}: 50px; width: 50px; height:
|
|
311
|
+
: 'left'}: 50px; width: 50px; height: 150px; transition: .3s"
|
|
251
312
|
>
|
|
313
|
+
<div
|
|
314
|
+
class="abs main-body-btn main-body-btn-ui"
|
|
315
|
+
style="top: 0px; ${true || (options.mode && options.mode.match('right')) ? 'right' : 'left'}: 0px"
|
|
316
|
+
>
|
|
317
|
+
<div class="abs center">
|
|
318
|
+
<i class="fas fa-caret-down main-body-btn-ui-open hide"></i>
|
|
319
|
+
<i class="fas fa-caret-up main-body-btn-ui-close"></i>
|
|
320
|
+
</div>
|
|
321
|
+
</div>
|
|
252
322
|
<div
|
|
253
323
|
class="abs main-body-btn main-body-btn-menu"
|
|
254
324
|
style="top: 50px; ${true || (options.mode && options.mode.match('right'))
|
|
@@ -261,15 +331,18 @@ const Modal = {
|
|
|
261
331
|
</div>
|
|
262
332
|
</div>
|
|
263
333
|
<div
|
|
264
|
-
class="abs main-body-btn main-body-btn-
|
|
265
|
-
|
|
334
|
+
class="abs main-body-btn main-body-btn-bar-custom ${options?.slideMenuTopBarBannerFix
|
|
335
|
+
? ''
|
|
336
|
+
: 'hide'}"
|
|
337
|
+
style="top: 100px; ${true || (options.mode && options.mode.match('right'))
|
|
338
|
+
? 'right'
|
|
339
|
+
: 'left'}: 0px"
|
|
266
340
|
>
|
|
267
341
|
<div class="abs center">
|
|
268
|
-
<i class="
|
|
269
|
-
<i class="
|
|
342
|
+
<i class="fa-solid fa-magnifying-glass main-body-btn-ui-bar-custom-open"></i>
|
|
343
|
+
<i class="fa-solid fa-home hide main-body-btn-ui-bar-custom-close"></i>
|
|
270
344
|
</div>
|
|
271
345
|
</div>
|
|
272
|
-
<div class="main-body-btn-container-custom"></div>
|
|
273
346
|
</div>
|
|
274
347
|
`,
|
|
275
348
|
);
|
|
@@ -278,6 +351,22 @@ const Modal = {
|
|
|
278
351
|
Modal.actionBtnCenter();
|
|
279
352
|
};
|
|
280
353
|
|
|
354
|
+
s(`.main-body-btn-bar-custom`).onclick = () => {
|
|
355
|
+
if (s(`.main-body-btn-ui-close`).classList.contains('hide')) {
|
|
356
|
+
s(`.main-body-btn-ui`).click();
|
|
357
|
+
}
|
|
358
|
+
if (s(`.main-body-btn-ui-bar-custom-open`).classList.contains('hide')) {
|
|
359
|
+
s(`.main-body-btn-ui-bar-custom-open`).classList.remove('hide');
|
|
360
|
+
s(`.main-body-btn-ui-bar-custom-close`).classList.add('hide');
|
|
361
|
+
s(`.slide-menu-top-bar-fix`).style.top = '0px';
|
|
362
|
+
} else {
|
|
363
|
+
s(`.main-body-btn-ui-bar-custom-open`).classList.add('hide');
|
|
364
|
+
s(`.main-body-btn-ui-bar-custom-close`).classList.remove('hide');
|
|
365
|
+
s(`.slide-menu-top-bar-fix`).style.top = '-100px';
|
|
366
|
+
s(`.top-bar-search-box-container`).click();
|
|
367
|
+
}
|
|
368
|
+
};
|
|
369
|
+
|
|
281
370
|
let _heightTopBar, _heightBottomBar, _topMenu;
|
|
282
371
|
s(`.main-body-btn-ui`).onclick = () => {
|
|
283
372
|
if (s(`.main-body-btn-ui-open`).classList.contains('hide')) {
|
|
@@ -323,6 +412,7 @@ const Modal = {
|
|
|
323
412
|
);
|
|
324
413
|
}
|
|
325
414
|
};
|
|
415
|
+
Modal.setTopBannerLink();
|
|
326
416
|
});
|
|
327
417
|
|
|
328
418
|
const inputSearchBoxId = `top-bar-search-box`;
|
|
@@ -407,9 +497,14 @@ const Modal = {
|
|
|
407
497
|
})}
|
|
408
498
|
</div>
|
|
409
499
|
</div>
|
|
410
|
-
${options?.
|
|
411
|
-
? html`<div
|
|
412
|
-
|
|
500
|
+
${options?.slideMenuTopBarBannerFix
|
|
501
|
+
? html`<div
|
|
502
|
+
class="abs modal slide-menu-top-bar-fix"
|
|
503
|
+
style="height: ${options.heightTopBar}px; top: 0px"
|
|
504
|
+
>
|
|
505
|
+
<a class="a-link-top-banner fl">
|
|
506
|
+
<div class="inl">${await options.slideMenuTopBarBannerFix()}</div></a
|
|
507
|
+
>
|
|
413
508
|
</div>`
|
|
414
509
|
: ''}
|
|
415
510
|
</div>`,
|
|
@@ -434,8 +529,14 @@ const Modal = {
|
|
|
434
529
|
rules: [] /*{ type: 'isEmpty' }, { type: 'isEmail' }*/,
|
|
435
530
|
},
|
|
436
531
|
];
|
|
437
|
-
|
|
438
|
-
let
|
|
532
|
+
// Reusable hover/focus controller for search history panel
|
|
533
|
+
let unbindDocSearch = null;
|
|
534
|
+
const hoverFocusCtl = EventsUI.HoverFocusController({
|
|
535
|
+
inputSelector: `.top-bar-search-box-container`,
|
|
536
|
+
panelSelector: `.${id}`,
|
|
537
|
+
activeElementId: inputSearchBoxId,
|
|
538
|
+
onDismiss: () => dismissSearchBox(),
|
|
539
|
+
});
|
|
439
540
|
let currentKeyBoardSearchBoxIndex = 0;
|
|
440
541
|
let results = [];
|
|
441
542
|
let historySearchBox = [];
|
|
@@ -507,6 +608,7 @@ const Modal = {
|
|
|
507
608
|
}),
|
|
508
609
|
);
|
|
509
610
|
s(`.search-result-btn-${result.routerId}`).onclick = () => {
|
|
611
|
+
if (!s(`.html-${searchBoxHistoryId}`) || !s(`.html-${searchBoxHistoryId}`).hasChildNodes()) return;
|
|
510
612
|
s(`.html-${searchBoxHistoryId}`).childNodes[currentKeyBoardSearchBoxIndex].classList.remove(
|
|
511
613
|
`main-btn-menu-active`,
|
|
512
614
|
);
|
|
@@ -520,6 +622,7 @@ const Modal = {
|
|
|
520
622
|
};
|
|
521
623
|
|
|
522
624
|
const getResultSearchBox = (validatorData) => {
|
|
625
|
+
if (!s(`.html-${searchBoxHistoryId}`) || !s(`.html-${searchBoxHistoryId}`).hasChildNodes()) return;
|
|
523
626
|
const { model, id } = validatorData;
|
|
524
627
|
switch (model) {
|
|
525
628
|
case 'search-box':
|
|
@@ -592,17 +695,29 @@ const Modal = {
|
|
|
592
695
|
const isSearchBoxActiveElement = isActiveElement(inputSearchBoxId);
|
|
593
696
|
checkHistoryBoxTitleStatus();
|
|
594
697
|
checkShortcutContainerInfoEnabled();
|
|
595
|
-
if (!isSearchBoxActiveElement && !
|
|
698
|
+
if (!isSearchBoxActiveElement && !hoverFocusCtl.shouldStay()) {
|
|
596
699
|
Modal.removeModal(searchBoxHistoryId);
|
|
597
700
|
return;
|
|
598
701
|
}
|
|
599
|
-
setTimeout(() =>
|
|
702
|
+
setTimeout(() => {
|
|
703
|
+
getResultSearchBox(validatorData);
|
|
704
|
+
|
|
705
|
+
if (
|
|
706
|
+
s(`.slide-menu-top-bar-fix`) &&
|
|
707
|
+
!s(`.main-body-btn-ui-bar-custom-open`).classList.contains('hide')
|
|
708
|
+
) {
|
|
709
|
+
s(`.main-body-btn-bar-custom`).click();
|
|
710
|
+
}
|
|
711
|
+
});
|
|
600
712
|
};
|
|
601
713
|
|
|
602
714
|
const getDefaultSearchBoxSelector = () => `.search-result-btn-${currentKeyBoardSearchBoxIndex}`;
|
|
603
715
|
|
|
604
716
|
const updateSearchBoxValue = (selector) => {
|
|
605
717
|
if (!selector) selector = getDefaultSearchBoxSelector();
|
|
718
|
+
// check exist childNodes
|
|
719
|
+
if (!s(selector) || !s(selector).hasChildNodes()) return;
|
|
720
|
+
|
|
606
721
|
if (s(selector).childNodes) {
|
|
607
722
|
if (
|
|
608
723
|
s(selector).childNodes[s(selector).childNodes.length - 1] &&
|
|
@@ -626,6 +741,10 @@ const Modal = {
|
|
|
626
741
|
|
|
627
742
|
const setSearchValue = (selector) => {
|
|
628
743
|
if (!selector) selector = getDefaultSearchBoxSelector();
|
|
744
|
+
|
|
745
|
+
// check exist childNodes
|
|
746
|
+
if (!s(selector) || !s(selector).hasChildNodes()) return;
|
|
747
|
+
|
|
629
748
|
historySearchBox = historySearchBox.filter(
|
|
630
749
|
(h) => h.routerId !== results[currentKeyBoardSearchBoxIndex].routerId,
|
|
631
750
|
);
|
|
@@ -676,27 +795,19 @@ const Modal = {
|
|
|
676
795
|
barMode: options.barMode,
|
|
677
796
|
});
|
|
678
797
|
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
798
|
+
// Bind hover/focus and click-outside to dismiss
|
|
799
|
+
hoverFocusCtl.bind();
|
|
800
|
+
unbindDocSearch = EventsUI.bindDismissOnDocumentClick({
|
|
801
|
+
shouldStay: hoverFocusCtl.shouldStay,
|
|
802
|
+
onDismiss: () => dismissSearchBox(),
|
|
803
|
+
anchors: [`.top-bar-search-box-container`, `.${id}`],
|
|
804
|
+
});
|
|
805
|
+
// Ensure cleanup when modal closes
|
|
806
|
+
Modal.Data[id].onCloseListener[`unbind-doc-${id}`] = () => unbindDocSearch && unbindDocSearch();
|
|
684
807
|
|
|
685
|
-
|
|
808
|
+
Modal.MoveTitleToBar(id);
|
|
686
809
|
|
|
687
|
-
|
|
688
|
-
hoverInputBox = true;
|
|
689
|
-
};
|
|
690
|
-
s(`.top-bar-search-box-container`).onmouseout = () => {
|
|
691
|
-
hoverInputBox = false;
|
|
692
|
-
};
|
|
693
|
-
s(`.${id}`).onmouseover = () => {
|
|
694
|
-
hoverHistBox = true;
|
|
695
|
-
};
|
|
696
|
-
s(`.${id}`).onmouseout = () => {
|
|
697
|
-
hoverHistBox = false;
|
|
698
|
-
s(`.${inputSearchBoxId}`).focus();
|
|
699
|
-
};
|
|
810
|
+
prepend(`.btn-bar-modal-container-${id}`, html`<div class="hide">${inputInfoNode.outerHTML}</div>`);
|
|
700
811
|
}
|
|
701
812
|
};
|
|
702
813
|
|
|
@@ -708,14 +819,23 @@ const Modal = {
|
|
|
708
819
|
searchBoxHistoryOpen();
|
|
709
820
|
searchBoxCallBack(formDataInfoNode[0]);
|
|
710
821
|
};
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
822
|
+
|
|
823
|
+
const dismissSearchBox = () => {
|
|
824
|
+
if (unbindDocSearch) {
|
|
825
|
+
try {
|
|
826
|
+
unbindDocSearch();
|
|
827
|
+
} catch (e) {}
|
|
714
828
|
}
|
|
829
|
+
Modal.removeModal(searchBoxHistoryId);
|
|
830
|
+
};
|
|
831
|
+
s('.top-bar-search-box').onblur = () => {
|
|
832
|
+
hoverFocusCtl.checkDismiss();
|
|
715
833
|
};
|
|
716
834
|
EventsUI.onClick(`.top-bar-search-box-container`, () => {
|
|
717
835
|
searchBoxHistoryOpen();
|
|
718
836
|
searchBoxCallBack(formDataInfoNode[0]);
|
|
837
|
+
const inputEl = s(`.${inputSearchBoxId}`);
|
|
838
|
+
if (inputEl && inputEl.focus) inputEl.focus();
|
|
719
839
|
});
|
|
720
840
|
|
|
721
841
|
const timePressDelay = 100;
|
|
@@ -880,7 +1000,11 @@ const Modal = {
|
|
|
880
1000
|
['Alt', 'k'],
|
|
881
1001
|
],
|
|
882
1002
|
eventCallBack: () => {
|
|
1003
|
+
if (isTextInputFocused()) return;
|
|
883
1004
|
if (s(`.top-bar-search-box`)) {
|
|
1005
|
+
if (s(`.main-body-btn-ui-close`).classList.contains('hide')) {
|
|
1006
|
+
s(`.main-body-btn-ui-open`).click();
|
|
1007
|
+
}
|
|
884
1008
|
s(`.top-bar-search-box`).blur();
|
|
885
1009
|
s(`.top-bar-search-box`).focus();
|
|
886
1010
|
s(`.top-bar-search-box`).select();
|
|
@@ -897,8 +1021,10 @@ const Modal = {
|
|
|
897
1021
|
barConfig.buttons.menu.disabled = true;
|
|
898
1022
|
barConfig.buttons.close.disabled = true;
|
|
899
1023
|
const id = 'bottom-bar';
|
|
900
|
-
if (
|
|
901
|
-
|
|
1024
|
+
if (!this.Data[idModal].homeModals) this.Data[idModal].homeModals = [];
|
|
1025
|
+
if (!this.Data[idModal].homeModals.includes(id)) {
|
|
1026
|
+
this.Data[idModal].homeModals.push(id);
|
|
1027
|
+
}
|
|
902
1028
|
const html = async () => html`
|
|
903
1029
|
<style>
|
|
904
1030
|
.top-bar-search-box-container {
|
|
@@ -1015,7 +1141,7 @@ const Modal = {
|
|
|
1015
1141
|
await Modal.onHomeRouterEvent();
|
|
1016
1142
|
Object.keys(this.Data[idModal].onHome).map((keyListener) => this.Data[idModal].onHome[keyListener]());
|
|
1017
1143
|
});
|
|
1018
|
-
EventsUI.onClick(`.action-btn-app-icon`, () =>
|
|
1144
|
+
EventsUI.onClick(`.action-btn-app-icon`, () => s(`.action-btn-home`).click());
|
|
1019
1145
|
Keyboard.instanceMultiPressKey({
|
|
1020
1146
|
id: 'input-shortcut-global-escape',
|
|
1021
1147
|
keys: ['Escape'],
|
|
@@ -1026,11 +1152,18 @@ const Modal = {
|
|
|
1026
1152
|
}
|
|
1027
1153
|
|
|
1028
1154
|
{
|
|
1029
|
-
ThemeEvents['action-btn-theme'] = () => {
|
|
1155
|
+
ThemeEvents['action-btn-theme'] = async () => {
|
|
1030
1156
|
htmls(
|
|
1031
1157
|
`.action-btn-theme-render`,
|
|
1032
1158
|
html` ${darkTheme ? html` <i class="fas fa-moon"></i>` : html`<i class="far fa-sun"></i>`}`,
|
|
1033
1159
|
);
|
|
1160
|
+
if (s(`.slide-menu-top-bar-fix`)) {
|
|
1161
|
+
htmls(
|
|
1162
|
+
`.slide-menu-top-bar-fix`,
|
|
1163
|
+
html`<a class="a-link-top-banner fl">${await options.slideMenuTopBarBannerFix()}</a>`,
|
|
1164
|
+
);
|
|
1165
|
+
Modal.setTopBannerLink();
|
|
1166
|
+
}
|
|
1034
1167
|
};
|
|
1035
1168
|
ThemeEvents['action-btn-theme']();
|
|
1036
1169
|
|
|
@@ -1048,13 +1181,80 @@ const Modal = {
|
|
|
1048
1181
|
|
|
1049
1182
|
{
|
|
1050
1183
|
htmls(`.action-btn-lang-render`, html` ${s('html').lang}`);
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1184
|
+
// old method
|
|
1185
|
+
// EventsUI.onClick(`.action-btn-lang`, () => {
|
|
1186
|
+
// let lang = 'en';
|
|
1187
|
+
// if (s('html').lang === 'en') lang = 'es';
|
|
1188
|
+
// if (s(`.dropdown-option-${lang}`))
|
|
1189
|
+
// DropDown.Tokens['settings-lang'].onClickEvents[`dropdown-option-${lang}`]();
|
|
1190
|
+
// else Translate.renderLang(lang);
|
|
1191
|
+
// });
|
|
1192
|
+
|
|
1193
|
+
// Open lightweight empty modal on language button, with shared dismiss logic
|
|
1194
|
+
EventsUI.onClick(
|
|
1195
|
+
`.action-btn-lang`,
|
|
1196
|
+
async () => {
|
|
1197
|
+
const id = 'action-btn-lang-modal';
|
|
1198
|
+
if (s(`.${id}`)) {
|
|
1199
|
+
return s(`.btn-close-${id}`).click();
|
|
1200
|
+
}
|
|
1201
|
+
const { barConfig } = await Themes[Css.currentTheme]();
|
|
1202
|
+
barConfig.buttons.maximize.disabled = true;
|
|
1203
|
+
barConfig.buttons.minimize.disabled = true;
|
|
1204
|
+
barConfig.buttons.restore.disabled = true;
|
|
1205
|
+
barConfig.buttons.menu.disabled = true;
|
|
1206
|
+
barConfig.buttons.close.disabled = false;
|
|
1207
|
+
await Modal.Render({
|
|
1208
|
+
id,
|
|
1209
|
+
barConfig,
|
|
1210
|
+
title: html`${renderViewTitle({
|
|
1211
|
+
icon: html`<i class="fas fa-language mini-title"></i>`,
|
|
1212
|
+
text: Translate.Render('select lang'),
|
|
1213
|
+
})}`,
|
|
1214
|
+
html: async () => html`${await Translate.RenderSetting('action-drop-modal' + id)}`,
|
|
1215
|
+
titleClass: 'mini-title',
|
|
1216
|
+
style: {
|
|
1217
|
+
resize: 'none',
|
|
1218
|
+
width: '100% !important',
|
|
1219
|
+
height: '350px !important',
|
|
1220
|
+
'max-width': '350px !important',
|
|
1221
|
+
'z-index': 7,
|
|
1222
|
+
},
|
|
1223
|
+
dragDisabled: true,
|
|
1224
|
+
maximize: true,
|
|
1225
|
+
heightBottomBar: 0,
|
|
1226
|
+
heightTopBar: originHeightTopBar,
|
|
1227
|
+
barMode: options.barMode,
|
|
1228
|
+
});
|
|
1229
|
+
|
|
1230
|
+
// Move title inside the bar container to align with control buttons
|
|
1231
|
+
Modal.MoveTitleToBar(id);
|
|
1232
|
+
|
|
1233
|
+
// Position the language selection modal relative to the language button
|
|
1234
|
+
Modal.positionRelativeToAnchor({
|
|
1235
|
+
modalSelector: `.${id}`,
|
|
1236
|
+
anchorSelector: '.action-btn-lang',
|
|
1237
|
+
align: 'right',
|
|
1238
|
+
offset: { x: 0, y: 6 },
|
|
1239
|
+
autoVertical: true,
|
|
1240
|
+
});
|
|
1241
|
+
|
|
1242
|
+
// Hover/focus controller uses the button as input anchor
|
|
1243
|
+
const hoverFocusCtl = EventsUI.HoverFocusController({
|
|
1244
|
+
inputSelector: `.action-btn-lang`,
|
|
1245
|
+
panelSelector: `.${id}`,
|
|
1246
|
+
onDismiss: () => Modal.removeModal(id),
|
|
1247
|
+
});
|
|
1248
|
+
hoverFocusCtl.bind();
|
|
1249
|
+
const unbindDoc = EventsUI.bindDismissOnDocumentClick({
|
|
1250
|
+
shouldStay: hoverFocusCtl.shouldStay,
|
|
1251
|
+
onDismiss: () => Modal.removeModal(id),
|
|
1252
|
+
anchors: [`.action-btn-lang`, `.${id}`],
|
|
1253
|
+
});
|
|
1254
|
+
Modal.Data[id].onCloseListener[`unbind-doc-${id}`] = () => unbindDoc();
|
|
1255
|
+
},
|
|
1256
|
+
{ context: 'modal', noGate: true, noLoading: true },
|
|
1257
|
+
);
|
|
1058
1258
|
}
|
|
1059
1259
|
|
|
1060
1260
|
{
|
|
@@ -1348,19 +1548,18 @@ const Modal = {
|
|
|
1348
1548
|
this.onHomeRouterEvent = async () => {
|
|
1349
1549
|
for (const keyModal of Object.keys(this.Data)) {
|
|
1350
1550
|
if (
|
|
1351
|
-
![idModal, 'main-body-top', 'main-body']
|
|
1352
|
-
.concat(options?.homeModals ? options.homeModals : [])
|
|
1353
|
-
.includes(keyModal)
|
|
1551
|
+
![idModal, 'main-body-top', 'main-body'].concat(this.Data[idModal]?.homeModals || []).includes(keyModal)
|
|
1354
1552
|
)
|
|
1355
|
-
s(`.btn-close-${keyModal}`).click();
|
|
1553
|
+
if (s(`.btn-close-${keyModal}`)) s(`.btn-close-${keyModal}`).click();
|
|
1356
1554
|
backMenuButtonEvent();
|
|
1357
1555
|
}
|
|
1358
|
-
s(`.btn-close-modal-menu`).click();
|
|
1556
|
+
if (s(`.btn-close-modal-menu`)) s(`.btn-close-modal-menu`).click();
|
|
1359
1557
|
setPath(getProxyPath());
|
|
1360
|
-
setDocTitle(
|
|
1558
|
+
setDocTitle();
|
|
1361
1559
|
};
|
|
1362
1560
|
s(`.main-btn-home`).onclick = async () => {
|
|
1363
|
-
await this.onHomeRouterEvent();
|
|
1561
|
+
// await this.onHomeRouterEvent();
|
|
1562
|
+
s(`.action-btn-home`).click();
|
|
1364
1563
|
};
|
|
1365
1564
|
EventsUI.onClick(`.btn-icon-menu-back`, backMenuButtonEvent);
|
|
1366
1565
|
EventsUI.onClick(`.btn-icon-menu-mode`, () => {
|
|
@@ -1429,42 +1628,139 @@ const Modal = {
|
|
|
1429
1628
|
default:
|
|
1430
1629
|
break;
|
|
1431
1630
|
}
|
|
1631
|
+
// Track drag position for consistency
|
|
1632
|
+
let dragPosition = { x: 0, y: 0 };
|
|
1633
|
+
|
|
1634
|
+
// Initialize drag options with proper bounds and smooth transitions
|
|
1432
1635
|
let dragOptions = {
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1636
|
+
handle: handle,
|
|
1637
|
+
bounds: {
|
|
1638
|
+
top: 0,
|
|
1639
|
+
left: 0,
|
|
1640
|
+
right: 0,
|
|
1641
|
+
bottom: 0,
|
|
1642
|
+
},
|
|
1643
|
+
preventDefault: true,
|
|
1644
|
+
position: { x: 0, y: 0 },
|
|
1645
|
+
onDragStart: () => {
|
|
1646
|
+
const modal = s(`.${idModal}`);
|
|
1647
|
+
if (!modal) return false; // Prevent drag if modal not found
|
|
1648
|
+
|
|
1649
|
+
// Store current position
|
|
1650
|
+
const computedStyle = window.getComputedStyle(modal);
|
|
1651
|
+
const matrix = new DOMMatrixReadOnly(computedStyle.transform);
|
|
1652
|
+
dragPosition = {
|
|
1653
|
+
x: matrix.m41 || 0,
|
|
1654
|
+
y: matrix.m42 || 0,
|
|
1655
|
+
};
|
|
1656
|
+
|
|
1657
|
+
modal.style.transition = 'none';
|
|
1658
|
+
modal.style.willChange = 'transform';
|
|
1659
|
+
return true; // Allow drag to start
|
|
1439
1660
|
},
|
|
1440
1661
|
onDrag: (data) => {
|
|
1441
|
-
|
|
1442
|
-
|
|
1662
|
+
// Update position based on drag delta
|
|
1663
|
+
dragPosition = { x: data.x, y: data.y };
|
|
1443
1664
|
},
|
|
1444
|
-
onDragEnd: (
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1665
|
+
onDragEnd: () => {
|
|
1666
|
+
const modal = s(`.${idModal}`);
|
|
1667
|
+
if (!modal) return;
|
|
1668
|
+
|
|
1669
|
+
modal.style.willChange = '';
|
|
1670
|
+
modal.style.transition = transition;
|
|
1671
|
+
|
|
1672
|
+
// Update drag instance with current position
|
|
1673
|
+
if (dragInstance) {
|
|
1674
|
+
dragInstance.updateOptions({
|
|
1675
|
+
position: dragPosition,
|
|
1676
|
+
});
|
|
1677
|
+
}
|
|
1678
|
+
|
|
1679
|
+
// Notify listeners
|
|
1680
|
+
Object.keys(this.Data[idModal].onDragEndListener || {}).forEach((keyListener) => {
|
|
1681
|
+
this.Data[idModal].onDragEndListener[keyListener]?.();
|
|
1682
|
+
});
|
|
1451
1683
|
},
|
|
1452
1684
|
};
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1685
|
+
|
|
1686
|
+
let dragInstance = null;
|
|
1687
|
+
|
|
1688
|
+
// Initialize or update drag instance
|
|
1689
|
+
const setDragInstance = () => {
|
|
1690
|
+
if (options?.dragDisabled) {
|
|
1691
|
+
if (dragInstance) {
|
|
1692
|
+
dragInstance.destroy();
|
|
1693
|
+
dragInstance = null;
|
|
1694
|
+
}
|
|
1695
|
+
return null;
|
|
1696
|
+
}
|
|
1697
|
+
|
|
1698
|
+
const modal = s(`.${idModal}`);
|
|
1699
|
+
if (!modal) {
|
|
1700
|
+
console.warn(`Modal element .${idModal} not found for drag initialization`);
|
|
1701
|
+
return null;
|
|
1702
|
+
}
|
|
1703
|
+
|
|
1704
|
+
// Ensure the modal has position: absolute for proper dragging
|
|
1705
|
+
if (window.getComputedStyle(modal).position !== 'absolute') {
|
|
1706
|
+
modal.style.position = 'absolute';
|
|
1707
|
+
}
|
|
1708
|
+
|
|
1709
|
+
// Clean up existing instance
|
|
1710
|
+
if (dragInstance) {
|
|
1711
|
+
dragInstance.destroy();
|
|
1712
|
+
}
|
|
1713
|
+
|
|
1714
|
+
try {
|
|
1715
|
+
// Create new instance with updated options
|
|
1716
|
+
dragInstance = new Draggable(modal, dragOptions);
|
|
1717
|
+
return dragInstance;
|
|
1718
|
+
} catch (error) {
|
|
1719
|
+
console.error('Failed to initialize draggable:', error);
|
|
1720
|
+
return null;
|
|
1721
|
+
}
|
|
1722
|
+
};
|
|
1723
|
+
|
|
1724
|
+
// Expose method to update drag options
|
|
1456
1725
|
this.Data[idModal].setDragInstance = (updateDragOptions) => {
|
|
1457
|
-
|
|
1458
|
-
...dragOptions,
|
|
1459
|
-
|
|
1460
|
-
};
|
|
1726
|
+
if (updateDragOptions) {
|
|
1727
|
+
dragOptions = { ...dragOptions, ...updateDragOptions };
|
|
1728
|
+
}
|
|
1461
1729
|
dragInstance = setDragInstance();
|
|
1462
1730
|
this.Data[idModal].dragInstance = dragInstance;
|
|
1463
1731
|
this.Data[idModal].dragOptions = dragOptions;
|
|
1464
1732
|
};
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1733
|
+
// Initialize modal with proper transitions
|
|
1734
|
+
const modal = s(`.${idModal}`);
|
|
1735
|
+
if (modal) {
|
|
1736
|
+
// Initial state
|
|
1737
|
+
modal.style.transition = 'opacity 0.15s ease, transform 0.3s ease';
|
|
1738
|
+
modal.style.opacity = '0';
|
|
1739
|
+
|
|
1740
|
+
// Trigger fade-in after a small delay to allow initial render
|
|
1741
|
+
requestAnimationFrame(() => {
|
|
1742
|
+
if (!modal) return;
|
|
1743
|
+
modal.style.opacity = '1';
|
|
1744
|
+
|
|
1745
|
+
// Set final transition after initial animation completes
|
|
1746
|
+
setTimeout(() => {
|
|
1747
|
+
if (modal) {
|
|
1748
|
+
modal.style.transition = transition;
|
|
1749
|
+
|
|
1750
|
+
// Initialize drag after transitions are set
|
|
1751
|
+
if (!options.dragDisabled) {
|
|
1752
|
+
setDragInstance();
|
|
1753
|
+
if (!options.mode) {
|
|
1754
|
+
dragInstance.updateOptions({
|
|
1755
|
+
position: { x: 0, y: 0 },
|
|
1756
|
+
disabled: false, // Ensure drag is enabled after restore
|
|
1757
|
+
});
|
|
1758
|
+
}
|
|
1759
|
+
}
|
|
1760
|
+
}
|
|
1761
|
+
}, 150);
|
|
1762
|
+
});
|
|
1763
|
+
}
|
|
1468
1764
|
|
|
1469
1765
|
const btnCloseEvent = () => {
|
|
1470
1766
|
Object.keys(this.Data[idModal].onCloseListener).map((keyListener) =>
|
|
@@ -1480,62 +1776,122 @@ const Modal = {
|
|
|
1480
1776
|
setTimeout(() => {
|
|
1481
1777
|
if (!s(`.${idModal}`)) return;
|
|
1482
1778
|
this.removeModal(idModal);
|
|
1483
|
-
//
|
|
1484
|
-
if (options.route)
|
|
1485
|
-
(
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
if (this.Data[subIdModal].options.route) {
|
|
1492
|
-
newPath = `${newPath}${this.Data[subIdModal].options.route}`;
|
|
1493
|
-
// console.warn('SET MODAL URI', newPath);
|
|
1494
|
-
setPath(newPath);
|
|
1495
|
-
this.setTopModalCallback(subIdModal);
|
|
1496
|
-
return setDocTitle({ ...options.RouterInstance, route: this.Data[subIdModal].options.route });
|
|
1497
|
-
}
|
|
1498
|
-
}
|
|
1499
|
-
// console.warn('SET MODAL URI', newPath);
|
|
1500
|
-
setPath(`${newPath}${Modal.homeCid ? `?cid=${Modal.homeCid}` : ''}`);
|
|
1501
|
-
return setDocTitle({ ...options.RouterInstance, route: '' });
|
|
1502
|
-
}
|
|
1503
|
-
})();
|
|
1779
|
+
// Handle modal route change
|
|
1780
|
+
if (options.route) {
|
|
1781
|
+
closeModalRouteChangeEvent({
|
|
1782
|
+
route: options.route,
|
|
1783
|
+
RouterInstance: options.RouterInstance,
|
|
1784
|
+
homeCid: Modal.homeCid,
|
|
1785
|
+
});
|
|
1786
|
+
}
|
|
1504
1787
|
}, 300);
|
|
1505
1788
|
};
|
|
1506
1789
|
s(`.btn-close-${idModal}`).onclick = btnCloseEvent;
|
|
1507
1790
|
|
|
1791
|
+
// Minimize button handler
|
|
1508
1792
|
s(`.btn-minimize-${idModal}`).onclick = () => {
|
|
1509
|
-
|
|
1510
|
-
|
|
1793
|
+
const modal = s(`.${idModal}`);
|
|
1794
|
+
if (!modal) return;
|
|
1795
|
+
|
|
1796
|
+
if (options.slideMenu) {
|
|
1797
|
+
delete this.Data[idModal].slideMenu;
|
|
1798
|
+
}
|
|
1799
|
+
|
|
1800
|
+
// Keep drag enabled when minimized
|
|
1801
|
+
if (dragInstance) {
|
|
1802
|
+
dragInstance.updateOptions({ disabled: false });
|
|
1803
|
+
}
|
|
1804
|
+
|
|
1805
|
+
// Set up transition
|
|
1806
|
+
modal.style.transition = 'height 0.3s ease, transform 0.3s ease';
|
|
1807
|
+
|
|
1808
|
+
// Update button states
|
|
1511
1809
|
s(`.btn-minimize-${idModal}`).style.display = 'none';
|
|
1512
1810
|
s(`.btn-maximize-${idModal}`).style.display = null;
|
|
1513
1811
|
s(`.btn-restore-${idModal}`).style.display = null;
|
|
1514
|
-
|
|
1515
|
-
|
|
1812
|
+
|
|
1813
|
+
// Collapse to header height
|
|
1814
|
+
const header = s(`.bar-default-modal-${idModal}`);
|
|
1815
|
+
if (header) {
|
|
1816
|
+
modal.style.height = `${header.clientHeight}px`;
|
|
1817
|
+
modal.style.overflow = 'hidden';
|
|
1818
|
+
}
|
|
1819
|
+
|
|
1820
|
+
// Restore transition after animation
|
|
1821
|
+
setTimeout(() => {
|
|
1822
|
+
if (modal) {
|
|
1823
|
+
modal.style.transition = transition;
|
|
1824
|
+
}
|
|
1825
|
+
}, 300);
|
|
1516
1826
|
};
|
|
1827
|
+
// Restore button handler
|
|
1517
1828
|
s(`.btn-restore-${idModal}`).onclick = () => {
|
|
1518
|
-
|
|
1519
|
-
|
|
1829
|
+
const modal = s(`.${idModal}`);
|
|
1830
|
+
if (!modal) return;
|
|
1831
|
+
|
|
1832
|
+
if (options.slideMenu) {
|
|
1833
|
+
delete this.Data[idModal].slideMenu;
|
|
1834
|
+
}
|
|
1835
|
+
|
|
1836
|
+
// Re-enable dragging
|
|
1837
|
+
if (dragInstance) {
|
|
1838
|
+
dragInstance.updateOptions({ disabled: false });
|
|
1839
|
+
}
|
|
1840
|
+
|
|
1841
|
+
// Set up transition
|
|
1842
|
+
modal.style.transition = 'all 0.3s ease';
|
|
1843
|
+
|
|
1844
|
+
// Update button states
|
|
1520
1845
|
s(`.btn-restore-${idModal}`).style.display = 'none';
|
|
1521
1846
|
s(`.btn-minimize-${idModal}`).style.display = null;
|
|
1522
1847
|
s(`.btn-maximize-${idModal}`).style.display = null;
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1848
|
+
|
|
1849
|
+
// Restore original dimensions and position
|
|
1850
|
+
modal.style.transform = '';
|
|
1851
|
+
modal.style.height = '';
|
|
1852
|
+
left = 0;
|
|
1853
|
+
width = 300;
|
|
1854
|
+
modal.style.left = `${left}px`;
|
|
1855
|
+
modal.style.width = `${width}px`;
|
|
1856
|
+
modal.style.overflow = '';
|
|
1857
|
+
|
|
1858
|
+
// Reset drag position
|
|
1859
|
+
dragPosition = { x: 0, y: 0 };
|
|
1860
|
+
|
|
1861
|
+
// Set new position
|
|
1862
|
+
modal.style.transform = `translate(0, 0)`;
|
|
1863
|
+
|
|
1864
|
+
// Adjust top position based on top bar visibility
|
|
1865
|
+
const heightDefaultTopBar = 40; // Default top bar height if not specified
|
|
1866
|
+
s(`.${idModal}`).style.top = s(`.main-body-btn-ui-close`).classList.contains('hide')
|
|
1867
|
+
? `0px`
|
|
1868
|
+
: `${options.heightTopBar ? options.heightTopBar : heightDefaultTopBar}px`;
|
|
1869
|
+
|
|
1870
|
+
// Re-enable drag after restore
|
|
1871
|
+
if (dragInstance) {
|
|
1872
|
+
dragInstance.updateOptions({
|
|
1873
|
+
position: { x: 0, y: 0 },
|
|
1874
|
+
disabled: false, // Ensure drag is enabled after restore
|
|
1875
|
+
});
|
|
1876
|
+
}
|
|
1877
|
+
setTimeout(() => (s(`.${idModal}`) ? (s(`.${idModal}`).style.transition = transition) : null), 300);
|
|
1531
1878
|
};
|
|
1532
1879
|
s(`.btn-maximize-${idModal}`).onclick = () => {
|
|
1533
|
-
s(`.${idModal}`)
|
|
1534
|
-
|
|
1880
|
+
const modal = s(`.${idModal}`);
|
|
1881
|
+
if (!modal) return;
|
|
1882
|
+
|
|
1883
|
+
// Disable drag when maximizing
|
|
1884
|
+
if (dragInstance) {
|
|
1885
|
+
dragInstance.updateOptions({ disabled: true });
|
|
1886
|
+
}
|
|
1887
|
+
|
|
1888
|
+
modal.style.transition = '0.3s';
|
|
1889
|
+
setTimeout(() => (modal ? (modal.style.transition = transition) : null), 300);
|
|
1890
|
+
|
|
1535
1891
|
s(`.btn-maximize-${idModal}`).style.display = 'none';
|
|
1536
1892
|
s(`.btn-restore-${idModal}`).style.display = null;
|
|
1537
1893
|
s(`.btn-minimize-${idModal}`).style.display = null;
|
|
1538
|
-
|
|
1894
|
+
modal.style.transform = null;
|
|
1539
1895
|
|
|
1540
1896
|
if (options.slideMenu) {
|
|
1541
1897
|
const idSlide = this.Data[options.slideMenu]['slide-menu']
|
|
@@ -1770,6 +2126,114 @@ const Modal = {
|
|
|
1770
2126
|
};
|
|
1771
2127
|
});
|
|
1772
2128
|
},
|
|
2129
|
+
// Move modal title element into the bar's render container so it aligns with control buttons
|
|
2130
|
+
/**
|
|
2131
|
+
* Position a modal relative to an anchor element
|
|
2132
|
+
* @param {Object} options - Positioning options
|
|
2133
|
+
* @param {string} options.modalSelector - CSS selector for the modal element
|
|
2134
|
+
* @param {string} options.anchorSelector - CSS selector for the anchor element
|
|
2135
|
+
* @param {Object} [options.offset={x: 0, y: 6}] - Offset from anchor
|
|
2136
|
+
* @param {string} [options.align='right'] - Horizontal alignment ('left' or 'right')
|
|
2137
|
+
* @param {boolean} [options.autoVertical=true] - Whether to automatically determine vertical position
|
|
2138
|
+
* @param {boolean} [options.placeAbove] - Force position above/below anchor (overrides autoVertical)
|
|
2139
|
+
*/
|
|
2140
|
+
positionRelativeToAnchor({
|
|
2141
|
+
modalSelector,
|
|
2142
|
+
anchorSelector,
|
|
2143
|
+
offset = { x: 0, y: 6 },
|
|
2144
|
+
align = 'right',
|
|
2145
|
+
autoVertical = true,
|
|
2146
|
+
placeAbove,
|
|
2147
|
+
}) {
|
|
2148
|
+
try {
|
|
2149
|
+
const modal = s(modalSelector);
|
|
2150
|
+
const anchor = s(anchorSelector);
|
|
2151
|
+
|
|
2152
|
+
if (!modal || !anchor || !anchor.getBoundingClientRect) return;
|
|
2153
|
+
|
|
2154
|
+
// First, position the modal near its final position but off-screen
|
|
2155
|
+
const arect = anchor.getBoundingClientRect();
|
|
2156
|
+
const vh = window.innerHeight;
|
|
2157
|
+
const vw = window.innerWidth;
|
|
2158
|
+
const safeMargin = 6;
|
|
2159
|
+
|
|
2160
|
+
// Determine vertical position
|
|
2161
|
+
let finalPlaceAbove = placeAbove;
|
|
2162
|
+
if (autoVertical && placeAbove === undefined) {
|
|
2163
|
+
const inBottomBar = anchor.closest && anchor.closest('.bottom-bar');
|
|
2164
|
+
const inTopBar = anchor.closest && anchor.closest('.slide-menu-top-bar');
|
|
2165
|
+
|
|
2166
|
+
if (inBottomBar) finalPlaceAbove = true;
|
|
2167
|
+
else if (inTopBar) finalPlaceAbove = false;
|
|
2168
|
+
else finalPlaceAbove = arect.top > vh / 2; // heuristic fallback
|
|
2169
|
+
}
|
|
2170
|
+
|
|
2171
|
+
// Set initial position (slightly offset from final position)
|
|
2172
|
+
const initialOffset = finalPlaceAbove ? 20 : -20;
|
|
2173
|
+
modal.style.position = 'fixed';
|
|
2174
|
+
modal.style.opacity = '0';
|
|
2175
|
+
modal.style.transition = 'opacity 150ms ease-out, transform 150ms ease-out';
|
|
2176
|
+
|
|
2177
|
+
// Position near the anchor but slightly offset
|
|
2178
|
+
modal.style.top = `${finalPlaceAbove ? arect.top - 40 : arect.bottom + 20}px`;
|
|
2179
|
+
modal.style.left = `${align === 'right' ? arect.right - 200 : arect.left}px`;
|
|
2180
|
+
modal.style.transform = 'translateY(0)';
|
|
2181
|
+
|
|
2182
|
+
// Force reflow to ensure initial styles are applied
|
|
2183
|
+
modal.offsetHeight;
|
|
2184
|
+
|
|
2185
|
+
// Now calculate final position
|
|
2186
|
+
const mrect = modal.getBoundingClientRect();
|
|
2187
|
+
|
|
2188
|
+
// Calculate final top position
|
|
2189
|
+
const top = finalPlaceAbove ? arect.top - mrect.height - offset.y : arect.bottom + offset.y;
|
|
2190
|
+
|
|
2191
|
+
// Calculate final left position based on alignment
|
|
2192
|
+
let left;
|
|
2193
|
+
if (align === 'right') {
|
|
2194
|
+
left = arect.right - mrect.width - offset.x; // align right edges
|
|
2195
|
+
} else {
|
|
2196
|
+
left = arect.left + offset.x; // align left edges
|
|
2197
|
+
}
|
|
2198
|
+
|
|
2199
|
+
// Ensure modal stays within viewport bounds
|
|
2200
|
+
left = Math.max(safeMargin, Math.min(left, vw - mrect.width - safeMargin));
|
|
2201
|
+
const finalTop = Math.max(safeMargin, Math.min(top, vh - mrect.height - safeMargin));
|
|
2202
|
+
|
|
2203
|
+
// Apply final position with smooth transition
|
|
2204
|
+
requestAnimationFrame(() => {
|
|
2205
|
+
modal.style.top = `${Math.round(finalTop)}px`;
|
|
2206
|
+
modal.style.left = `${Math.round(left)}px`;
|
|
2207
|
+
modal.style.opacity = '1';
|
|
2208
|
+
});
|
|
2209
|
+
} catch (e) {
|
|
2210
|
+
console.error('Error positioning modal:', e);
|
|
2211
|
+
}
|
|
2212
|
+
},
|
|
2213
|
+
|
|
2214
|
+
MoveTitleToBar(idModal) {
|
|
2215
|
+
try {
|
|
2216
|
+
const titleEl = s(`.title-modal-${idModal}`);
|
|
2217
|
+
const container = s(`.btn-bar-modal-container-render-${idModal}`);
|
|
2218
|
+
if (!titleEl || !container) return;
|
|
2219
|
+
const titleNode = titleEl.cloneNode(true);
|
|
2220
|
+
titleEl.remove();
|
|
2221
|
+
container.classList.add('in');
|
|
2222
|
+
container.classList.add('fll');
|
|
2223
|
+
container.appendChild(titleNode);
|
|
2224
|
+
} catch (e) {
|
|
2225
|
+
// non-fatal: keep default placement if structure not present
|
|
2226
|
+
}
|
|
2227
|
+
},
|
|
2228
|
+
setTopBannerLink: function () {
|
|
2229
|
+
if (s(`.a-link-top-banner`)) {
|
|
2230
|
+
s(`.a-link-top-banner`).setAttribute('href', `${location.origin}${getProxyPath()}`);
|
|
2231
|
+
EventsUI.onClick(`.a-link-top-banner`, (e) => {
|
|
2232
|
+
e.preventDefault();
|
|
2233
|
+
s(`.action-btn-home`).click();
|
|
2234
|
+
});
|
|
2235
|
+
}
|
|
2236
|
+
},
|
|
1773
2237
|
headerTitleHeight: 40,
|
|
1774
2238
|
actionBtnCenter: function () {
|
|
1775
2239
|
if (!s(`.btn-close-modal-menu`).classList.contains('hide')) {
|