underpost 2.8.843 → 2.8.844
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/.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} +1 -1
- package/.github/workflows/{pwa-microservices-template.test.yml → pwa-microservices-template-test.ci.yml} +1 -1
- package/.vscode/settings.json +0 -1
- package/README.md +12 -2
- package/bin/build.js +8 -5
- package/bin/deploy.js +10 -69
- package/bin/file.js +15 -11
- package/cli.md +47 -43
- package/docker-compose.yml +1 -1
- package/manifests/deployment/dd-template-development/deployment.yaml +2 -2
- package/manifests/maas/gpu-diag.sh +1 -1
- package/package.json +3 -4
- package/src/api/user/user.router.js +24 -1
- package/src/cli/cluster.js +19 -10
- package/src/cli/index.js +1 -0
- package/src/cli/run.js +21 -0
- package/src/client/components/core/CssCore.js +0 -4
- package/src/client/components/core/Docs.js +1 -47
- package/src/client/components/core/EventsUI.js +92 -5
- package/src/client/components/core/Modal.js +431 -87
- package/src/client/components/core/NotificationManager.js +2 -2
- package/src/client/components/core/Panel.js +2 -2
- package/src/client/components/core/PanelForm.js +2 -0
- package/src/client/components/core/Recover.js +1 -1
- package/src/index.js +1 -1
- package/src/server/client-build.js +0 -20
- package/src/server/valkey.js +102 -41
|
@@ -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()) {
|
|
@@ -434,8 +436,14 @@ const Modal = {
|
|
|
434
436
|
rules: [] /*{ type: 'isEmpty' }, { type: 'isEmail' }*/,
|
|
435
437
|
},
|
|
436
438
|
];
|
|
437
|
-
|
|
438
|
-
let
|
|
439
|
+
// Reusable hover/focus controller for search history panel
|
|
440
|
+
let unbindDocSearch = null;
|
|
441
|
+
const hoverFocusCtl = EventsUI.HoverFocusController({
|
|
442
|
+
inputSelector: `.top-bar-search-box-container`,
|
|
443
|
+
panelSelector: `.${id}`,
|
|
444
|
+
activeElementId: inputSearchBoxId,
|
|
445
|
+
onDismiss: () => dismissSearchBox(),
|
|
446
|
+
});
|
|
439
447
|
let currentKeyBoardSearchBoxIndex = 0;
|
|
440
448
|
let results = [];
|
|
441
449
|
let historySearchBox = [];
|
|
@@ -592,7 +600,7 @@ const Modal = {
|
|
|
592
600
|
const isSearchBoxActiveElement = isActiveElement(inputSearchBoxId);
|
|
593
601
|
checkHistoryBoxTitleStatus();
|
|
594
602
|
checkShortcutContainerInfoEnabled();
|
|
595
|
-
if (!isSearchBoxActiveElement && !
|
|
603
|
+
if (!isSearchBoxActiveElement && !hoverFocusCtl.shouldStay()) {
|
|
596
604
|
Modal.removeModal(searchBoxHistoryId);
|
|
597
605
|
return;
|
|
598
606
|
}
|
|
@@ -676,27 +684,19 @@ const Modal = {
|
|
|
676
684
|
barMode: options.barMode,
|
|
677
685
|
});
|
|
678
686
|
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
687
|
+
// Bind hover/focus and click-outside to dismiss
|
|
688
|
+
hoverFocusCtl.bind();
|
|
689
|
+
unbindDocSearch = EventsUI.bindDismissOnDocumentClick({
|
|
690
|
+
shouldStay: hoverFocusCtl.shouldStay,
|
|
691
|
+
onDismiss: () => dismissSearchBox(),
|
|
692
|
+
anchors: [`.top-bar-search-box-container`, `.${id}`],
|
|
693
|
+
});
|
|
694
|
+
// Ensure cleanup when modal closes
|
|
695
|
+
Modal.Data[id].onCloseListener[`unbind-doc-${id}`] = () => unbindDocSearch && unbindDocSearch();
|
|
684
696
|
|
|
685
|
-
|
|
697
|
+
Modal.MoveTitleToBar(id);
|
|
686
698
|
|
|
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
|
-
};
|
|
699
|
+
prepend(`.btn-bar-modal-container-${id}`, html`<div class="hide">${inputInfoNode.outerHTML}</div>`);
|
|
700
700
|
}
|
|
701
701
|
};
|
|
702
702
|
|
|
@@ -708,14 +708,23 @@ const Modal = {
|
|
|
708
708
|
searchBoxHistoryOpen();
|
|
709
709
|
searchBoxCallBack(formDataInfoNode[0]);
|
|
710
710
|
};
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
711
|
+
|
|
712
|
+
const dismissSearchBox = () => {
|
|
713
|
+
if (unbindDocSearch) {
|
|
714
|
+
try {
|
|
715
|
+
unbindDocSearch();
|
|
716
|
+
} catch (e) {}
|
|
714
717
|
}
|
|
718
|
+
Modal.removeModal(searchBoxHistoryId);
|
|
719
|
+
};
|
|
720
|
+
s('.top-bar-search-box').onblur = () => {
|
|
721
|
+
hoverFocusCtl.checkDismiss();
|
|
715
722
|
};
|
|
716
723
|
EventsUI.onClick(`.top-bar-search-box-container`, () => {
|
|
717
724
|
searchBoxHistoryOpen();
|
|
718
725
|
searchBoxCallBack(formDataInfoNode[0]);
|
|
726
|
+
const inputEl = s(`.${inputSearchBoxId}`);
|
|
727
|
+
if (inputEl && inputEl.focus) inputEl.focus();
|
|
719
728
|
});
|
|
720
729
|
|
|
721
730
|
const timePressDelay = 100;
|
|
@@ -897,8 +906,10 @@ const Modal = {
|
|
|
897
906
|
barConfig.buttons.menu.disabled = true;
|
|
898
907
|
barConfig.buttons.close.disabled = true;
|
|
899
908
|
const id = 'bottom-bar';
|
|
900
|
-
if (
|
|
901
|
-
|
|
909
|
+
if (!this.Data[idModal].homeModals) this.Data[idModal].homeModals = [];
|
|
910
|
+
if (!this.Data[idModal].homeModals.includes(id)) {
|
|
911
|
+
this.Data[idModal].homeModals.push(id);
|
|
912
|
+
}
|
|
902
913
|
const html = async () => html`
|
|
903
914
|
<style>
|
|
904
915
|
.top-bar-search-box-container {
|
|
@@ -1048,13 +1059,79 @@ const Modal = {
|
|
|
1048
1059
|
|
|
1049
1060
|
{
|
|
1050
1061
|
htmls(`.action-btn-lang-render`, html` ${s('html').lang}`);
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1062
|
+
// old method
|
|
1063
|
+
// EventsUI.onClick(`.action-btn-lang`, () => {
|
|
1064
|
+
// let lang = 'en';
|
|
1065
|
+
// if (s('html').lang === 'en') lang = 'es';
|
|
1066
|
+
// if (s(`.dropdown-option-${lang}`))
|
|
1067
|
+
// DropDown.Tokens['settings-lang'].onClickEvents[`dropdown-option-${lang}`]();
|
|
1068
|
+
// else Translate.renderLang(lang);
|
|
1069
|
+
// });
|
|
1070
|
+
|
|
1071
|
+
// Open lightweight empty modal on language button, with shared dismiss logic
|
|
1072
|
+
EventsUI.onClick(
|
|
1073
|
+
`.action-btn-lang`,
|
|
1074
|
+
async () => {
|
|
1075
|
+
const id = 'action-btn-lang-modal';
|
|
1076
|
+
if (s(`.${id}`)) {
|
|
1077
|
+
return s(`.btn-close-${id}`).click();
|
|
1078
|
+
}
|
|
1079
|
+
const { barConfig } = await Themes[Css.currentTheme]();
|
|
1080
|
+
barConfig.buttons.maximize.disabled = true;
|
|
1081
|
+
barConfig.buttons.minimize.disabled = true;
|
|
1082
|
+
barConfig.buttons.restore.disabled = true;
|
|
1083
|
+
barConfig.buttons.menu.disabled = true;
|
|
1084
|
+
barConfig.buttons.close.disabled = false;
|
|
1085
|
+
await Modal.Render({
|
|
1086
|
+
id,
|
|
1087
|
+
barConfig,
|
|
1088
|
+
title: html`${renderViewTitle({
|
|
1089
|
+
icon: html`<i class="fas fa-language mini-title"></i>`,
|
|
1090
|
+
text: Translate.Render('select lang'),
|
|
1091
|
+
})}`,
|
|
1092
|
+
html: () => html``,
|
|
1093
|
+
titleClass: 'mini-title',
|
|
1094
|
+
style: {
|
|
1095
|
+
resize: 'none',
|
|
1096
|
+
'max-width': '300px',
|
|
1097
|
+
height: '150px !important',
|
|
1098
|
+
'z-index': 7,
|
|
1099
|
+
},
|
|
1100
|
+
dragDisabled: true,
|
|
1101
|
+
maximize: true,
|
|
1102
|
+
heightBottomBar: 0,
|
|
1103
|
+
heightTopBar: originHeightTopBar,
|
|
1104
|
+
barMode: options.barMode,
|
|
1105
|
+
});
|
|
1106
|
+
|
|
1107
|
+
// Move title inside the bar container to align with control buttons
|
|
1108
|
+
Modal.MoveTitleToBar(id);
|
|
1109
|
+
|
|
1110
|
+
// Position the language selection modal relative to the language button
|
|
1111
|
+
Modal.positionRelativeToAnchor({
|
|
1112
|
+
modalSelector: `.${id}`,
|
|
1113
|
+
anchorSelector: '.action-btn-lang',
|
|
1114
|
+
align: 'right',
|
|
1115
|
+
offset: { x: 0, y: 6 },
|
|
1116
|
+
autoVertical: true,
|
|
1117
|
+
});
|
|
1118
|
+
|
|
1119
|
+
// Hover/focus controller uses the button as input anchor
|
|
1120
|
+
const hoverFocusCtl = EventsUI.HoverFocusController({
|
|
1121
|
+
inputSelector: `.action-btn-lang`,
|
|
1122
|
+
panelSelector: `.${id}`,
|
|
1123
|
+
onDismiss: () => Modal.removeModal(id),
|
|
1124
|
+
});
|
|
1125
|
+
hoverFocusCtl.bind();
|
|
1126
|
+
const unbindDoc = EventsUI.bindDismissOnDocumentClick({
|
|
1127
|
+
shouldStay: hoverFocusCtl.shouldStay,
|
|
1128
|
+
onDismiss: () => Modal.removeModal(id),
|
|
1129
|
+
anchors: [`.action-btn-lang`, `.${id}`],
|
|
1130
|
+
});
|
|
1131
|
+
Modal.Data[id].onCloseListener[`unbind-doc-${id}`] = () => unbindDoc();
|
|
1132
|
+
},
|
|
1133
|
+
{ context: 'modal', noGate: true, noLoading: true },
|
|
1134
|
+
);
|
|
1058
1135
|
}
|
|
1059
1136
|
|
|
1060
1137
|
{
|
|
@@ -1348,9 +1425,7 @@ const Modal = {
|
|
|
1348
1425
|
this.onHomeRouterEvent = async () => {
|
|
1349
1426
|
for (const keyModal of Object.keys(this.Data)) {
|
|
1350
1427
|
if (
|
|
1351
|
-
![idModal, 'main-body-top', 'main-body']
|
|
1352
|
-
.concat(options?.homeModals ? options.homeModals : [])
|
|
1353
|
-
.includes(keyModal)
|
|
1428
|
+
![idModal, 'main-body-top', 'main-body'].concat(this.Data[idModal]?.homeModals || []).includes(keyModal)
|
|
1354
1429
|
)
|
|
1355
1430
|
s(`.btn-close-${keyModal}`).click();
|
|
1356
1431
|
backMenuButtonEvent();
|
|
@@ -1429,42 +1504,139 @@ const Modal = {
|
|
|
1429
1504
|
default:
|
|
1430
1505
|
break;
|
|
1431
1506
|
}
|
|
1507
|
+
// Track drag position for consistency
|
|
1508
|
+
let dragPosition = { x: 0, y: 0 };
|
|
1509
|
+
|
|
1510
|
+
// Initialize drag options with proper bounds and smooth transitions
|
|
1432
1511
|
let dragOptions = {
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1512
|
+
handle: handle,
|
|
1513
|
+
bounds: {
|
|
1514
|
+
top: 0,
|
|
1515
|
+
left: 0,
|
|
1516
|
+
right: 0,
|
|
1517
|
+
bottom: 0,
|
|
1518
|
+
},
|
|
1519
|
+
preventDefault: true,
|
|
1520
|
+
position: { x: 0, y: 0 },
|
|
1521
|
+
onDragStart: () => {
|
|
1522
|
+
const modal = s(`.${idModal}`);
|
|
1523
|
+
if (!modal) return false; // Prevent drag if modal not found
|
|
1524
|
+
|
|
1525
|
+
// Store current position
|
|
1526
|
+
const computedStyle = window.getComputedStyle(modal);
|
|
1527
|
+
const matrix = new DOMMatrixReadOnly(computedStyle.transform);
|
|
1528
|
+
dragPosition = {
|
|
1529
|
+
x: matrix.m41 || 0,
|
|
1530
|
+
y: matrix.m42 || 0,
|
|
1531
|
+
};
|
|
1532
|
+
|
|
1533
|
+
modal.style.transition = 'none';
|
|
1534
|
+
modal.style.willChange = 'transform';
|
|
1535
|
+
return true; // Allow drag to start
|
|
1439
1536
|
},
|
|
1440
1537
|
onDrag: (data) => {
|
|
1441
|
-
|
|
1442
|
-
|
|
1538
|
+
// Update position based on drag delta
|
|
1539
|
+
dragPosition = { x: data.x, y: data.y };
|
|
1443
1540
|
},
|
|
1444
|
-
onDragEnd: (
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1541
|
+
onDragEnd: () => {
|
|
1542
|
+
const modal = s(`.${idModal}`);
|
|
1543
|
+
if (!modal) return;
|
|
1544
|
+
|
|
1545
|
+
modal.style.willChange = '';
|
|
1546
|
+
modal.style.transition = transition;
|
|
1547
|
+
|
|
1548
|
+
// Update drag instance with current position
|
|
1549
|
+
if (dragInstance) {
|
|
1550
|
+
dragInstance.updateOptions({
|
|
1551
|
+
position: dragPosition,
|
|
1552
|
+
});
|
|
1553
|
+
}
|
|
1554
|
+
|
|
1555
|
+
// Notify listeners
|
|
1556
|
+
Object.keys(this.Data[idModal].onDragEndListener || {}).forEach((keyListener) => {
|
|
1557
|
+
this.Data[idModal].onDragEndListener[keyListener]?.();
|
|
1558
|
+
});
|
|
1451
1559
|
},
|
|
1452
1560
|
};
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1561
|
+
|
|
1562
|
+
let dragInstance = null;
|
|
1563
|
+
|
|
1564
|
+
// Initialize or update drag instance
|
|
1565
|
+
const setDragInstance = () => {
|
|
1566
|
+
if (options?.dragDisabled) {
|
|
1567
|
+
if (dragInstance) {
|
|
1568
|
+
dragInstance.destroy();
|
|
1569
|
+
dragInstance = null;
|
|
1570
|
+
}
|
|
1571
|
+
return null;
|
|
1572
|
+
}
|
|
1573
|
+
|
|
1574
|
+
const modal = s(`.${idModal}`);
|
|
1575
|
+
if (!modal) {
|
|
1576
|
+
console.warn(`Modal element .${idModal} not found for drag initialization`);
|
|
1577
|
+
return null;
|
|
1578
|
+
}
|
|
1579
|
+
|
|
1580
|
+
// Ensure the modal has position: absolute for proper dragging
|
|
1581
|
+
if (window.getComputedStyle(modal).position !== 'absolute') {
|
|
1582
|
+
modal.style.position = 'absolute';
|
|
1583
|
+
}
|
|
1584
|
+
|
|
1585
|
+
// Clean up existing instance
|
|
1586
|
+
if (dragInstance) {
|
|
1587
|
+
dragInstance.destroy();
|
|
1588
|
+
}
|
|
1589
|
+
|
|
1590
|
+
try {
|
|
1591
|
+
// Create new instance with updated options
|
|
1592
|
+
dragInstance = new Draggable(modal, dragOptions);
|
|
1593
|
+
return dragInstance;
|
|
1594
|
+
} catch (error) {
|
|
1595
|
+
console.error('Failed to initialize draggable:', error);
|
|
1596
|
+
return null;
|
|
1597
|
+
}
|
|
1598
|
+
};
|
|
1599
|
+
|
|
1600
|
+
// Expose method to update drag options
|
|
1456
1601
|
this.Data[idModal].setDragInstance = (updateDragOptions) => {
|
|
1457
|
-
|
|
1458
|
-
...dragOptions,
|
|
1459
|
-
|
|
1460
|
-
};
|
|
1602
|
+
if (updateDragOptions) {
|
|
1603
|
+
dragOptions = { ...dragOptions, ...updateDragOptions };
|
|
1604
|
+
}
|
|
1461
1605
|
dragInstance = setDragInstance();
|
|
1462
1606
|
this.Data[idModal].dragInstance = dragInstance;
|
|
1463
1607
|
this.Data[idModal].dragOptions = dragOptions;
|
|
1464
1608
|
};
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1609
|
+
// Initialize modal with proper transitions
|
|
1610
|
+
const modal = s(`.${idModal}`);
|
|
1611
|
+
if (modal) {
|
|
1612
|
+
// Initial state
|
|
1613
|
+
modal.style.transition = 'opacity 0.15s ease, transform 0.3s ease';
|
|
1614
|
+
modal.style.opacity = '0';
|
|
1615
|
+
|
|
1616
|
+
// Trigger fade-in after a small delay to allow initial render
|
|
1617
|
+
requestAnimationFrame(() => {
|
|
1618
|
+
if (!modal) return;
|
|
1619
|
+
modal.style.opacity = '1';
|
|
1620
|
+
|
|
1621
|
+
// Set final transition after initial animation completes
|
|
1622
|
+
setTimeout(() => {
|
|
1623
|
+
if (modal) {
|
|
1624
|
+
modal.style.transition = transition;
|
|
1625
|
+
|
|
1626
|
+
// Initialize drag after transitions are set
|
|
1627
|
+
if (!options.dragDisabled) {
|
|
1628
|
+
setDragInstance();
|
|
1629
|
+
if (!options.mode) {
|
|
1630
|
+
dragInstance.updateOptions({
|
|
1631
|
+
position: { x: 0, y: 0 },
|
|
1632
|
+
disabled: false, // Ensure drag is enabled after restore
|
|
1633
|
+
});
|
|
1634
|
+
}
|
|
1635
|
+
}
|
|
1636
|
+
}
|
|
1637
|
+
}, 150);
|
|
1638
|
+
});
|
|
1639
|
+
}
|
|
1468
1640
|
|
|
1469
1641
|
const btnCloseEvent = () => {
|
|
1470
1642
|
Object.keys(this.Data[idModal].onCloseListener).map((keyListener) =>
|
|
@@ -1490,13 +1662,13 @@ const Modal = {
|
|
|
1490
1662
|
for (const subIdModal of Object.keys(this.Data).reverse()) {
|
|
1491
1663
|
if (this.Data[subIdModal].options.route) {
|
|
1492
1664
|
newPath = `${newPath}${this.Data[subIdModal].options.route}`;
|
|
1493
|
-
|
|
1665
|
+
console.warn('------------> SET MODAL URI', newPath);
|
|
1494
1666
|
setPath(newPath);
|
|
1495
1667
|
this.setTopModalCallback(subIdModal);
|
|
1496
1668
|
return setDocTitle({ ...options.RouterInstance, route: this.Data[subIdModal].options.route });
|
|
1497
1669
|
}
|
|
1498
1670
|
}
|
|
1499
|
-
|
|
1671
|
+
console.warn('-------------> SET MODAL URI', newPath);
|
|
1500
1672
|
setPath(`${newPath}${Modal.homeCid ? `?cid=${Modal.homeCid}` : ''}`);
|
|
1501
1673
|
return setDocTitle({ ...options.RouterInstance, route: '' });
|
|
1502
1674
|
}
|
|
@@ -1505,37 +1677,110 @@ const Modal = {
|
|
|
1505
1677
|
};
|
|
1506
1678
|
s(`.btn-close-${idModal}`).onclick = btnCloseEvent;
|
|
1507
1679
|
|
|
1680
|
+
// Minimize button handler
|
|
1508
1681
|
s(`.btn-minimize-${idModal}`).onclick = () => {
|
|
1509
|
-
|
|
1510
|
-
|
|
1682
|
+
const modal = s(`.${idModal}`);
|
|
1683
|
+
if (!modal) return;
|
|
1684
|
+
|
|
1685
|
+
if (options.slideMenu) {
|
|
1686
|
+
delete this.Data[idModal].slideMenu;
|
|
1687
|
+
}
|
|
1688
|
+
|
|
1689
|
+
// Keep drag enabled when minimized
|
|
1690
|
+
if (dragInstance) {
|
|
1691
|
+
dragInstance.updateOptions({ disabled: false });
|
|
1692
|
+
}
|
|
1693
|
+
|
|
1694
|
+
// Set up transition
|
|
1695
|
+
modal.style.transition = 'height 0.3s ease, transform 0.3s ease';
|
|
1696
|
+
|
|
1697
|
+
// Update button states
|
|
1511
1698
|
s(`.btn-minimize-${idModal}`).style.display = 'none';
|
|
1512
1699
|
s(`.btn-maximize-${idModal}`).style.display = null;
|
|
1513
1700
|
s(`.btn-restore-${idModal}`).style.display = null;
|
|
1514
|
-
|
|
1515
|
-
|
|
1701
|
+
|
|
1702
|
+
// Collapse to header height
|
|
1703
|
+
const header = s(`.bar-default-modal-${idModal}`);
|
|
1704
|
+
if (header) {
|
|
1705
|
+
modal.style.height = `${header.clientHeight}px`;
|
|
1706
|
+
modal.style.overflow = 'hidden';
|
|
1707
|
+
}
|
|
1708
|
+
|
|
1709
|
+
// Restore transition after animation
|
|
1710
|
+
setTimeout(() => {
|
|
1711
|
+
if (modal) {
|
|
1712
|
+
modal.style.transition = transition;
|
|
1713
|
+
}
|
|
1714
|
+
}, 300);
|
|
1516
1715
|
};
|
|
1716
|
+
// Restore button handler
|
|
1517
1717
|
s(`.btn-restore-${idModal}`).onclick = () => {
|
|
1518
|
-
|
|
1519
|
-
|
|
1718
|
+
const modal = s(`.${idModal}`);
|
|
1719
|
+
if (!modal) return;
|
|
1720
|
+
|
|
1721
|
+
if (options.slideMenu) {
|
|
1722
|
+
delete this.Data[idModal].slideMenu;
|
|
1723
|
+
}
|
|
1724
|
+
|
|
1725
|
+
// Re-enable dragging
|
|
1726
|
+
if (dragInstance) {
|
|
1727
|
+
dragInstance.updateOptions({ disabled: false });
|
|
1728
|
+
}
|
|
1729
|
+
|
|
1730
|
+
// Set up transition
|
|
1731
|
+
modal.style.transition = 'all 0.3s ease';
|
|
1732
|
+
|
|
1733
|
+
// Update button states
|
|
1520
1734
|
s(`.btn-restore-${idModal}`).style.display = 'none';
|
|
1521
1735
|
s(`.btn-minimize-${idModal}`).style.display = null;
|
|
1522
1736
|
s(`.btn-maximize-${idModal}`).style.display = null;
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1737
|
+
|
|
1738
|
+
// Restore original dimensions and position
|
|
1739
|
+
modal.style.transform = '';
|
|
1740
|
+
modal.style.height = '';
|
|
1741
|
+
left = 0;
|
|
1742
|
+
width = 300;
|
|
1743
|
+
modal.style.left = `${left}px`;
|
|
1744
|
+
modal.style.width = `${width}px`;
|
|
1745
|
+
modal.style.overflow = '';
|
|
1746
|
+
|
|
1747
|
+
// Reset drag position
|
|
1748
|
+
dragPosition = { x: 0, y: 0 };
|
|
1749
|
+
|
|
1750
|
+
// Set new position
|
|
1751
|
+
modal.style.transform = `translate(0, 0)`;
|
|
1752
|
+
|
|
1753
|
+
// Adjust top position based on top bar visibility
|
|
1754
|
+
const heightDefaultTopBar = 40; // Default top bar height if not specified
|
|
1755
|
+
s(`.${idModal}`).style.top = s(`.main-body-btn-ui-close`).classList.contains('hide')
|
|
1756
|
+
? `0px`
|
|
1757
|
+
: `${options.heightTopBar ? options.heightTopBar : heightDefaultTopBar}px`;
|
|
1758
|
+
|
|
1759
|
+
// Re-enable drag after restore
|
|
1760
|
+
if (dragInstance) {
|
|
1761
|
+
dragInstance.updateOptions({
|
|
1762
|
+
position: { x: 0, y: 0 },
|
|
1763
|
+
disabled: false, // Ensure drag is enabled after restore
|
|
1764
|
+
});
|
|
1765
|
+
}
|
|
1766
|
+
setTimeout(() => (s(`.${idModal}`) ? (s(`.${idModal}`).style.transition = transition) : null), 300);
|
|
1531
1767
|
};
|
|
1532
1768
|
s(`.btn-maximize-${idModal}`).onclick = () => {
|
|
1533
|
-
s(`.${idModal}`)
|
|
1534
|
-
|
|
1769
|
+
const modal = s(`.${idModal}`);
|
|
1770
|
+
if (!modal) return;
|
|
1771
|
+
|
|
1772
|
+
// Disable drag when maximizing
|
|
1773
|
+
if (dragInstance) {
|
|
1774
|
+
dragInstance.updateOptions({ disabled: true });
|
|
1775
|
+
}
|
|
1776
|
+
|
|
1777
|
+
modal.style.transition = '0.3s';
|
|
1778
|
+
setTimeout(() => (modal ? (modal.style.transition = transition) : null), 300);
|
|
1779
|
+
|
|
1535
1780
|
s(`.btn-maximize-${idModal}`).style.display = 'none';
|
|
1536
1781
|
s(`.btn-restore-${idModal}`).style.display = null;
|
|
1537
1782
|
s(`.btn-minimize-${idModal}`).style.display = null;
|
|
1538
|
-
|
|
1783
|
+
modal.style.transform = null;
|
|
1539
1784
|
|
|
1540
1785
|
if (options.slideMenu) {
|
|
1541
1786
|
const idSlide = this.Data[options.slideMenu]['slide-menu']
|
|
@@ -1770,6 +2015,105 @@ const Modal = {
|
|
|
1770
2015
|
};
|
|
1771
2016
|
});
|
|
1772
2017
|
},
|
|
2018
|
+
// Move modal title element into the bar's render container so it aligns with control buttons
|
|
2019
|
+
/**
|
|
2020
|
+
* Position a modal relative to an anchor element
|
|
2021
|
+
* @param {Object} options - Positioning options
|
|
2022
|
+
* @param {string} options.modalSelector - CSS selector for the modal element
|
|
2023
|
+
* @param {string} options.anchorSelector - CSS selector for the anchor element
|
|
2024
|
+
* @param {Object} [options.offset={x: 0, y: 6}] - Offset from anchor
|
|
2025
|
+
* @param {string} [options.align='right'] - Horizontal alignment ('left' or 'right')
|
|
2026
|
+
* @param {boolean} [options.autoVertical=true] - Whether to automatically determine vertical position
|
|
2027
|
+
* @param {boolean} [options.placeAbove] - Force position above/below anchor (overrides autoVertical)
|
|
2028
|
+
*/
|
|
2029
|
+
positionRelativeToAnchor({
|
|
2030
|
+
modalSelector,
|
|
2031
|
+
anchorSelector,
|
|
2032
|
+
offset = { x: 0, y: 6 },
|
|
2033
|
+
align = 'right',
|
|
2034
|
+
autoVertical = true,
|
|
2035
|
+
placeAbove,
|
|
2036
|
+
}) {
|
|
2037
|
+
try {
|
|
2038
|
+
const modal = s(modalSelector);
|
|
2039
|
+
const anchor = s(anchorSelector);
|
|
2040
|
+
|
|
2041
|
+
if (!modal || !anchor || !anchor.getBoundingClientRect) return;
|
|
2042
|
+
|
|
2043
|
+
// First, position the modal near its final position but off-screen
|
|
2044
|
+
const arect = anchor.getBoundingClientRect();
|
|
2045
|
+
const vh = window.innerHeight;
|
|
2046
|
+
const vw = window.innerWidth;
|
|
2047
|
+
const safeMargin = 6;
|
|
2048
|
+
|
|
2049
|
+
// Determine vertical position
|
|
2050
|
+
let finalPlaceAbove = placeAbove;
|
|
2051
|
+
if (autoVertical && placeAbove === undefined) {
|
|
2052
|
+
const inBottomBar = anchor.closest && anchor.closest('.bottom-bar');
|
|
2053
|
+
const inTopBar = anchor.closest && anchor.closest('.slide-menu-top-bar');
|
|
2054
|
+
|
|
2055
|
+
if (inBottomBar) finalPlaceAbove = true;
|
|
2056
|
+
else if (inTopBar) finalPlaceAbove = false;
|
|
2057
|
+
else finalPlaceAbove = arect.top > vh / 2; // heuristic fallback
|
|
2058
|
+
}
|
|
2059
|
+
|
|
2060
|
+
// Set initial position (slightly offset from final position)
|
|
2061
|
+
const initialOffset = finalPlaceAbove ? 20 : -20;
|
|
2062
|
+
modal.style.position = 'fixed';
|
|
2063
|
+
modal.style.opacity = '0';
|
|
2064
|
+
modal.style.transition = 'opacity 150ms ease-out, transform 150ms ease-out';
|
|
2065
|
+
|
|
2066
|
+
// Position near the anchor but slightly offset
|
|
2067
|
+
modal.style.top = `${finalPlaceAbove ? arect.top - 40 : arect.bottom + 20}px`;
|
|
2068
|
+
modal.style.left = `${align === 'right' ? arect.right - 200 : arect.left}px`;
|
|
2069
|
+
modal.style.transform = 'translateY(0)';
|
|
2070
|
+
|
|
2071
|
+
// Force reflow to ensure initial styles are applied
|
|
2072
|
+
modal.offsetHeight;
|
|
2073
|
+
|
|
2074
|
+
// Now calculate final position
|
|
2075
|
+
const mrect = modal.getBoundingClientRect();
|
|
2076
|
+
|
|
2077
|
+
// Calculate final top position
|
|
2078
|
+
const top = finalPlaceAbove ? arect.top - mrect.height - offset.y : arect.bottom + offset.y;
|
|
2079
|
+
|
|
2080
|
+
// Calculate final left position based on alignment
|
|
2081
|
+
let left;
|
|
2082
|
+
if (align === 'right') {
|
|
2083
|
+
left = arect.right - mrect.width - offset.x; // align right edges
|
|
2084
|
+
} else {
|
|
2085
|
+
left = arect.left + offset.x; // align left edges
|
|
2086
|
+
}
|
|
2087
|
+
|
|
2088
|
+
// Ensure modal stays within viewport bounds
|
|
2089
|
+
left = Math.max(safeMargin, Math.min(left, vw - mrect.width - safeMargin));
|
|
2090
|
+
const finalTop = Math.max(safeMargin, Math.min(top, vh - mrect.height - safeMargin));
|
|
2091
|
+
|
|
2092
|
+
// Apply final position with smooth transition
|
|
2093
|
+
requestAnimationFrame(() => {
|
|
2094
|
+
modal.style.top = `${Math.round(finalTop)}px`;
|
|
2095
|
+
modal.style.left = `${Math.round(left)}px`;
|
|
2096
|
+
modal.style.opacity = '1';
|
|
2097
|
+
});
|
|
2098
|
+
} catch (e) {
|
|
2099
|
+
console.error('Error positioning modal:', e);
|
|
2100
|
+
}
|
|
2101
|
+
},
|
|
2102
|
+
|
|
2103
|
+
MoveTitleToBar(idModal) {
|
|
2104
|
+
try {
|
|
2105
|
+
const titleEl = s(`.title-modal-${idModal}`);
|
|
2106
|
+
const container = s(`.btn-bar-modal-container-render-${idModal}`);
|
|
2107
|
+
if (!titleEl || !container) return;
|
|
2108
|
+
const titleNode = titleEl.cloneNode(true);
|
|
2109
|
+
titleEl.remove();
|
|
2110
|
+
container.classList.add('in');
|
|
2111
|
+
container.classList.add('fll');
|
|
2112
|
+
container.appendChild(titleNode);
|
|
2113
|
+
} catch (e) {
|
|
2114
|
+
// non-fatal: keep default placement if structure not present
|
|
2115
|
+
}
|
|
2116
|
+
},
|
|
1773
2117
|
headerTitleHeight: 40,
|
|
1774
2118
|
actionBtnCenter: function () {
|
|
1775
2119
|
if (!s(`.btn-close-modal-menu`).classList.contains('hide')) {
|
|
@@ -15,10 +15,10 @@ const NotificationManager = {
|
|
|
15
15
|
right: 5px !important;
|
|
16
16
|
width: 300px !important;
|
|
17
17
|
bottom: ${5 + (options?.heightBottomBar ? options.heightBottomBar : 0)}px !important;
|
|
18
|
-
z-index:
|
|
18
|
+
z-index: 11 !important;
|
|
19
19
|
}
|
|
20
20
|
.notification-board-title {
|
|
21
|
-
padding:
|
|
21
|
+
padding: 11px !important;
|
|
22
22
|
}
|
|
23
23
|
.notification-manager-date {
|
|
24
24
|
font-size: 20px !important;
|