estreui 1.2.6 → 1.4.0

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/index.html CHANGED
@@ -64,6 +64,7 @@
64
64
  <script defer type="text/javascript" src="./scripts/estreUi-core.js"></script>
65
65
  <script defer type="text/javascript" src="./scripts/estreUi-dialog.js"></script>
66
66
  <script defer type="text/javascript" src="./scripts/estreUi-notation.js"></script>
67
+ <script defer type="text/javascript" src="./scripts/estreUi-notification.js"></script>
67
68
  <script defer type="text/javascript" src="./scripts/estreUi-pageModel.js"></script>
68
69
  <script defer type="text/javascript" src="./scripts/estreUi-pageManager.js"></script>
69
70
  <script defer type="text/javascript" src="./scripts/estreUi-handles.js"></script>
@@ -81,6 +82,7 @@
81
82
  <link rel="preload" as="fetch" type="text/html" href="./staticDoc.html" crossOrigin="anonymous" />
82
83
  <link rel="preload" as="fetch" type="text/html" href="./instantDoc.html" crossOrigin="anonymous" />
83
84
  <link rel="preload" as="fetch" type="text/html" href="./mainMenu.html" crossOrigin="anonymous" />
85
+ <link rel="preload" as="fetch" type="text/html" href="./overwatchPanel.html" crossOrigin="anonymous" />
84
86
  <link rel="preload" as="fetch" type="text/html" href="./stockHandlePrototypes.html" crossOrigin="anonymous" />
85
87
  <link rel="preload" as="fetch" type="text/html" href="./customHandlePrototypes.html" crossOrigin="anonymous" />
86
88
 
@@ -97,6 +99,22 @@
97
99
  </head>
98
100
 
99
101
  <body class="vfv_scroll">
102
+ <!--
103
+ Dark mode pre-paint coupling (opt-in): uncomment to write
104
+ body[data-dark-mode] before the splash paints, avoiding FOLM
105
+ (flash of light mode) on dark-locked sessions. Adjust the
106
+ storage key and auto-mode policy to match your project.
107
+ -->
108
+ <!--
109
+ <script>
110
+ (function () {
111
+ const stored = localStorage.getItem("estreUi.darkMode");
112
+ const dark = stored === "1"
113
+ || (stored == null && window.matchMedia && matchMedia("(prefers-color-scheme: dark)").matches);
114
+ if (dark) document.body.dataset.darkMode = "1";
115
+ })();
116
+ </script>
117
+ -->
100
118
  <main id="splashRoot" style="z-index: 500; ">
101
119
  <section id="splash" data-on-top="0">
102
120
  <div class="container" data-container-id="root">
@@ -113,6 +131,9 @@
113
131
  </header>
114
132
  <nav id="mainMenu" class="right" data-exported="1" data-opened="">
115
133
  </nav>
134
+ <nav id="overwatchPanel" data-exported="1" data-opened="">
135
+ </nav>
136
+ <section id="panelTrigger" data-static="1"></section>
116
137
 
117
138
  <div id="ptr"><div>
118
139
  <!-- Commented out. This is not implemented currently. <dotlottie-player src="./lotties/ptr_indic.json" background="transparent" speed="1" direction="1" mode="normal"></dotlottie-player> -->
@@ -199,12 +199,13 @@
199
199
  <div class="container top" data-container-id="noti" data-static="1" data-on-top="1">
200
200
  <article class="" data-article-id="noti" data-static="" data-multi-instance="1" data-instance-origin="">
201
201
  <div class="h_icon_set post_block" data-bind-attr="interactive@data-interactive" data-bind-style="bgColor@--bg-color">
202
- <div class="icon_place"><img data-bind-attr="iconSrc@src" /></div>
202
+ <div class="icon_place"><img data-bind-attr="largeIconSrc@src" /></div>
203
203
  <div class="content_place">
204
204
  <div class="title_line"><span data-bind="contentTitle"></span></div>
205
+ <div class="subtitle_line"><span data-bind="subtitle"></span></div>
205
206
  <div class="h_icon_Set content_area">
206
207
  <div class="content_place" data-bind="content" data-bind-style="textColor@--color textSize@--size textWeight@--weight"></div>
207
- <div class="icon_place"><img data-bind-attr="subIconSrc@src" /></div>
208
+ <div class="icon_place"><img data-bind-attr="iconSrc@src" /></div>
208
209
  </div>
209
210
  </div>
210
211
  </div>
@@ -0,0 +1,27 @@
1
+ <header id="panelHeader" data-static="1">
2
+ <!-- Common top header. Clock / date populated by setOverwatchPanelClock in a later commit. -->
3
+ <span id="panelClock"></span>
4
+ <span id="panelDate"></span>
5
+ </header>
6
+ <div class="dynamic_section_host" data-static="1">
7
+ <div class="host_item" data-id="timeline"><span>Timeline</span></div>
8
+ <div class="host_item" data-id="quickPanel"><span>Quick</span></div>
9
+ </div>
10
+ <div class="dynamic_section_block full_screen" data-static="1">
11
+ <section class="block_item" id="timeline" data-id="timeline" data-static="1">
12
+ <!-- Timeline slot: markup reserved in roadmap #008. Implementation scheduled separately. -->
13
+ </section>
14
+ <section class="block_item" id="quickPanel" data-id="quickPanel" data-static="1">
15
+ <div class="container" data-container-id="root" data-static="1">
16
+ <article data-article-id="main" data-static="1">
17
+ <div class="quick_tiles">
18
+ <button id="darkModeToggle" class="quick_tile" type="button" data-dark-mode-state="auto" onclick="estreUi.cycleDarkMode();">
19
+ <span class="tile_icon" aria-hidden="true">&#x1F313;</span>
20
+ <span class="tile_label">Auto</span>
21
+ </button>
22
+ </div>
23
+ </article>
24
+ </div>
25
+ </section>
26
+ </div>
27
+ <section id="panelGrabArea" data-static="1"><div class="handle"></div><div class="pad"></div></section>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "estreui",
3
- "version": "1.2.6",
3
+ "version": "1.4.0",
4
4
  "description": "EstreUI Core Library - A comprehensive UI framework for web applications",
5
5
  "main": "scripts/estreUi.js",
6
6
  "files": [
@@ -502,10 +502,7 @@ const arrived = function (instanceOrigin) {
502
502
  }
503
503
 
504
504
 
505
-
506
- const noti = function (title, htmlContent, onTakeInteraction = (intent) => {}, mainIconSrc, subIconSrc) {
507
- //<= To do implement
508
- }
505
+ // noti() moved to estreUi-notification.js (roadmap #009).
509
506
 
510
507
 
511
508
  // ======================================================================
@@ -31,6 +31,11 @@ const estreUi = {
31
31
  menuSectionList: [],
32
32
  get menuArea() { return this.menuSections["menuArea"]; },
33
33
 
34
+ panelSections: {},
35
+ panelSectionList: [],
36
+ get quickPanel() { return this.panelSections["quickPanel"]; },
37
+ get timeline() { return this.panelSections["timeline"]; },
38
+
34
39
  headerSections: {},
35
40
  headerSectionList: [],
36
41
  get appbar() { return this.headerSections["appbar"]; },
@@ -41,6 +46,7 @@ const estreUi = {
41
46
  blindedCurrentOnTop: null,
42
47
  mainCurrentOnTop: null,
43
48
  menuCurrentOnTop: null,
49
+ panelCurrentOnTop: null,
44
50
  headerCurrentOnTop: null,
45
51
 
46
52
  //static getter
@@ -102,6 +108,16 @@ const estreUi = {
102
108
  $menuArea: null,
103
109
  $grabArea: null,
104
110
 
111
+ $overwatchPanel: null,
112
+ get $panelSections() { return this.$panelBlock?.find(c.c + se + uis.blockItem) ?? $(); },
113
+ $panelHeader: null,
114
+ $panelHost: null,
115
+ $panelBlock: null,
116
+ $panelClock: null,
117
+ $panelDate: null,
118
+ $panelGrabArea: null,
119
+ $panelTrigger: null,
120
+
105
121
  $fixedTop: null,
106
122
  get $headerSections() { return this.$fixedTop.find(c.c + se); },
107
123
  $appbar: null,
@@ -121,6 +137,11 @@ const estreUi = {
121
137
 
122
138
  //handles
123
139
  menuSwipeHandler: null,
140
+ panelOpenSwipeHandler: null,
141
+ panelCloseSwipeHandler: null,
142
+ panelClockTimeoutId: null,
143
+ panelClockIntervalId: null,
144
+ darkModeMql: null,
124
145
 
125
146
  //properties
126
147
  euiState: "exit",
@@ -166,6 +187,16 @@ const estreUi = {
166
187
  //getter and setter
167
188
  get isOpenMainMenu() { return this.$mainMenu.attr(eds.opened) == t1; },
168
189
 
190
+ get isOpenOverwatchPanel() { return this.$overwatchPanel.attr(eds.opened) == t1; },
191
+
192
+ get darkMode() {
193
+ const stored = localStorage.getItem("estreUi.darkMode");
194
+ if (stored == "1") return true;
195
+ if (stored == "0") return false;
196
+ return null;
197
+ },
198
+ get isDarkMode() { return document.body.dataset.darkMode == t1; },
199
+
169
200
 
170
201
 
171
202
  //links (object redirection)
@@ -187,6 +218,9 @@ const estreUi = {
187
218
 
188
219
  this.$mainMenu = $("nav#mainMenu");
189
220
 
221
+ this.$overwatchPanel = $("nav#overwatchPanel");
222
+ this.$panelTrigger = $("section#panelTrigger");
223
+
190
224
  this.$fixedTop = $("header#fixedTop");
191
225
 
192
226
  this.$fixedBottom = $("#fixedBottom");
@@ -198,6 +232,7 @@ const estreUi = {
198
232
  this.setReload();
199
233
  this.setBackNavigation();
200
234
  this.setMenuSwipeHandler();
235
+ this.setupDarkMode();
201
236
 
202
237
 
203
238
  const onLoadedFixedBottom = async _ => {
@@ -236,6 +271,23 @@ const estreUi = {
236
271
  return this.initStaticMenus(subTerm);
237
272
  }
238
273
 
274
+ const onLoadedOverwatchPanel = subTerm => {
275
+ this.$panelHeader = this.$overwatchPanel.find("header#panelHeader");
276
+ this.$panelHost = this.$overwatchPanel.find(uis.dynamicSectionHost);
277
+ this.$panelBlock = this.$overwatchPanel.find(uis.dynamicSectionBlock);
278
+ this.$panelClock = this.$panelHeader.find("#panelClock");
279
+ this.$panelDate = this.$panelHeader.find("#panelDate");
280
+ this.$panelGrabArea = this.$overwatchPanel.find("section#panelGrabArea");
281
+
282
+ this.$panelGrabArea.click(this.overwatchPanelGrabAreaOnclick);
283
+ this.setPanelSwipeHandler();
284
+ this.scheduleOverwatchPanelClock();
285
+ this.initOverwatchPanelHandles();
286
+ this.initOverwatchPanelTimeline();
287
+ this.updateDarkModeToggleWidgets();
288
+ return this.initStaticPanels(subTerm);
289
+ }
290
+
239
291
 
240
292
  const loadExported = url => fetch(url).then(response => {
241
293
  if (!response.ok) throw new Error('Network response was not ok');
@@ -315,6 +367,18 @@ const estreUi = {
315
367
  .then(() => loadExportedMainMenu(subTerm, attempt + 1));
316
368
  });
317
369
 
370
+ let loadExportedOverwatchPanel;
371
+ loadExportedOverwatchPanel = (subTerm, attempt = 0) => loadExported("overwatchPanel.html").then(htmlContent => {
372
+ this.$overwatchPanel.prepend(htmlContent);
373
+ return onLoadedOverwatchPanel(subTerm);
374
+ }).catch(error => {
375
+ const delay = Math.min(1000 * Math.pow(2, attempt), 30000);
376
+ console.error("There has been a problem with your fetch operation for overwatchPanel: ", error);
377
+ console.log(`Retrying to load overwatchPanel in ${delay}ms...`);
378
+ return postPromise(resolve => setTimeout(resolve, delay))
379
+ .then(() => loadExportedOverwatchPanel(subTerm, attempt + 1));
380
+ });
381
+
318
382
  let loadExportedStockHandlePrototypes;
319
383
  loadExportedStockHandlePrototypes = (_, attempt = 0) => loadExported("stockHandlePrototypes.html").then(htmlContent => {
320
384
  this.$handlePrototypes.prepend(htmlContent);
@@ -372,6 +436,7 @@ const estreUi = {
372
436
  await Promise.all(mainLoader);
373
437
 
374
438
  await (this.$mainMenu.attr(eds.exported) == t1 ? loadExportedMainMenu(subTerm) : onLoadedMainMenu(subTerm));
439
+ await (this.$overwatchPanel.attr(eds.exported) == t1 ? loadExportedOverwatchPanel(subTerm) : onLoadedOverwatchPanel(subTerm));
375
440
 
376
441
  this.initSessionManager();
377
442
 
@@ -512,6 +577,95 @@ const estreUi = {
512
577
  },
513
578
 
514
579
 
580
+ //dark mode
581
+ setupDarkMode() {
582
+ if (window.matchMedia) {
583
+ this.darkModeMql = window.matchMedia("(prefers-color-scheme: dark)");
584
+ const onChange = _ => { if (this.darkMode == null) this.applyDarkMode(); };
585
+ if (this.darkModeMql.addEventListener) this.darkModeMql.addEventListener("change", onChange);
586
+ else this.darkModeMql.addListener(onChange);
587
+ }
588
+ this.applyDarkMode();
589
+ },
590
+
591
+ setDarkMode(value) {
592
+ let pref;
593
+ if (value == null) pref = null;
594
+ else if (value === false || value === 0 || value === "0") pref = false;
595
+ else pref = true;
596
+
597
+ if (pref == null) localStorage.removeItem("estreUi.darkMode");
598
+ else localStorage.setItem("estreUi.darkMode", pref ? "1" : "0");
599
+
600
+ this.applyDarkMode();
601
+ return this.isDarkMode;
602
+ },
603
+
604
+ applyDarkMode() {
605
+ const pref = this.darkMode;
606
+ const active = (pref == null) ? (this.darkModeMql?.matches ?? false) : pref;
607
+ if (active) document.body.dataset.darkMode = "1";
608
+ else delete document.body.dataset.darkMode;
609
+ this.updateDarkModeToggleWidgets();
610
+ },
611
+
612
+ // Cycle auto -> light -> dark -> auto (single-button 3-state control)
613
+ cycleDarkMode() {
614
+ const pref = this.darkMode;
615
+ if (pref == null) this.setDarkMode(false);
616
+ else if (pref === false) this.setDarkMode(true);
617
+ else this.setDarkMode(null);
618
+ return this.darkMode;
619
+ },
620
+
621
+ /**
622
+ * Register a tile in the quickPanel section of overwatchPanel.
623
+ * Host projects call this after estreUi.init to append custom toggles/shortcuts.
624
+ *
625
+ * @param {Object} config
626
+ * @param {string} config.id unique DOM id for the tile
627
+ * @param {string} [config.icon] short glyph or emoji shown in the .tile_icon span
628
+ * @param {string} [config.label] text label shown in the .tile_label span
629
+ * @param {Function} [config.onClick] click handler; receives the jQuery event
630
+ * @returns {HTMLElement|null} the tile element, or null if quickPanel is not ready / id collides
631
+ */
632
+ registerOverwatchPanelTile(config) {
633
+ if (config == null || config.id == null) return null;
634
+ const $tiles = this.$overwatchPanel?.find("#quickPanel .quick_tiles");
635
+ if ($tiles == null || $tiles.length < 1) return null;
636
+ if ($tiles.find("#" + config.id).length > 0) return null;
637
+ const $tile = $("<button>").addClass("quick_tile").attr("type", "button").attr("id", config.id);
638
+ $tile.append($("<span>").addClass("tile_icon").attr("aria-hidden", "true").text(config.icon ?? ""));
639
+ $tile.append($("<span>").addClass("tile_label").text(config.label ?? ""));
640
+ if (typeof config.onClick == "function") $tile.on("click", config.onClick);
641
+ $tiles.append($tile);
642
+ return $tile[0];
643
+ },
644
+
645
+ unregisterOverwatchPanelTile(id) {
646
+ if (id == null) return false;
647
+ const $tile = this.$overwatchPanel?.find("#quickPanel .quick_tiles #" + id);
648
+ if ($tile == null || $tile.length < 1) return false;
649
+ $tile.off("click").remove();
650
+ return true;
651
+ },
652
+
653
+ updateDarkModeToggleWidgets() {
654
+ const $widgets = $("#darkModeToggle");
655
+ if ($widgets.length < 1) return;
656
+ const pref = this.darkMode;
657
+ const state = (pref == null) ? "auto" : (pref ? "dark" : "light");
658
+ const icon = state == "light" ? "\u2600\uFE0F" : (state == "dark" ? "\u263D" : "\u{1F313}");
659
+ const label = state.charAt(0).toUpperCase() + state.slice(1);
660
+ $widgets.each(function() {
661
+ const $w = $(this);
662
+ $w.attr("data-dark-mode-state", state);
663
+ $w.find(".tile_icon").text(icon);
664
+ $w.find(".tile_label").text(label);
665
+ });
666
+ },
667
+
668
+
515
669
  //mainMenu
516
670
  setMenuSwipeHandler() {
517
671
  if (this.$mainMenu.length > 0) {
@@ -534,6 +688,88 @@ const estreUi = {
534
688
  if (this.menuSwipeHandler != null) this.menuSwipeHandler.release();
535
689
  },
536
690
 
691
+
692
+ //overwatchPanel
693
+ setPanelSwipeHandler() {
694
+ if (this.$overwatchPanel.length < 1) return;
695
+ this.releasePanelSwipeHandler();
696
+ const ui = this;
697
+ if (this.$panelTrigger.length > 0) {
698
+ this.panelOpenSwipeHandler = new EstreSwipeHandler(this.$panelTrigger[0]).unuseX()
699
+ .setResponseBound(this.$overwatchPanel)
700
+ .setOnUp(function(grabX, grabY, handled, canceled, directed) {
701
+ if (handled && grabY > 0 && !ui.isOpenOverwatchPanel) {
702
+ setTimeout(_ => ui.openOverwatchPanel(), 0);
703
+ }
704
+ });
705
+ }
706
+ if (this.$panelGrabArea.length > 0) {
707
+ this.panelCloseSwipeHandler = new EstreSwipeHandler(this.$panelGrabArea[0]).unuseX()
708
+ .setResponseBound(this.$overwatchPanel)
709
+ .setOnUp(function(grabX, grabY, handled, canceled, directed) {
710
+ if (handled && grabY < 0 && ui.isOpenOverwatchPanel) {
711
+ setTimeout(_ => ui.closeOverwatchPanel(), 0);
712
+ }
713
+ });
714
+ }
715
+ },
716
+
717
+ releasePanelSwipeHandler() {
718
+ if (this.panelOpenSwipeHandler != null) { this.panelOpenSwipeHandler.release(); this.panelOpenSwipeHandler = null; }
719
+ if (this.panelCloseSwipeHandler != null) { this.panelCloseSwipeHandler.release(); this.panelCloseSwipeHandler = null; }
720
+ },
721
+
722
+ // The .dynamic_section_block inside overwatchPanel lives outside any <article>,
723
+ // so the standard article-scoped handle init never reaches it. Attach the handle
724
+ // here with the panel itself acting as a minimal host.
725
+ initOverwatchPanelHandles() {
726
+ if (this.$overwatchPanel.length < 1) return;
727
+ const $block = this.$panelBlock;
728
+ if ($block == null || $block.length < 1) return;
729
+ const host = { $host: this.$overwatchPanel };
730
+ new EstreDynamicSectionBlockHandle($block[0], host).init();
731
+ },
732
+
733
+ // Mounts EstreTimelineView onto overwatchPanel #timeline slot (roadmap #010).
734
+ // Timeline persists entries left by checkOut()ed noti banners.
735
+ initOverwatchPanelTimeline() {
736
+ if (typeof EstreTimelineView === "undefined") return;
737
+ if (this.$overwatchPanel == null || this.$overwatchPanel.length < 1) return;
738
+ const $timeline = this.$overwatchPanel.find("#timeline");
739
+ if ($timeline.length < 1) return;
740
+ this.timelineView = new EstreTimelineView($timeline);
741
+ },
742
+
743
+ setOverwatchPanelClock() {
744
+ if (this.$panelClock == null) return;
745
+ const now = new Date();
746
+ if (this.$panelClock.length > 0) {
747
+ const hh = String(now.getHours()).padStart(2, "0");
748
+ const mm = String(now.getMinutes()).padStart(2, "0");
749
+ this.$panelClock.text(hh + ":" + mm);
750
+ }
751
+ if (this.$panelDate.length > 0) {
752
+ const fmt = new Intl.DateTimeFormat(undefined, { weekday: "short", month: "short", day: "numeric" });
753
+ this.$panelDate.text(fmt.format(now));
754
+ }
755
+ },
756
+
757
+ scheduleOverwatchPanelClock() {
758
+ this.releaseOverwatchPanelClock();
759
+ this.setOverwatchPanelClock();
760
+ const now = new Date();
761
+ const msToNext = 60000 - (now.getSeconds() * 1000 + now.getMilliseconds());
762
+ this.panelClockTimeoutId = setTimeout(_ => {
763
+ this.setOverwatchPanelClock();
764
+ this.panelClockIntervalId = setInterval(_ => this.setOverwatchPanelClock(), 60000);
765
+ }, msToNext);
766
+ },
767
+
768
+ releaseOverwatchPanelClock() {
769
+ if (this.panelClockTimeoutId != null) { clearTimeout(this.panelClockTimeoutId); this.panelClockTimeoutId = null; }
770
+ if (this.panelClockIntervalId != null) { clearInterval(this.panelClockIntervalId); this.panelClockIntervalId = null; }
771
+ },
772
+
537
773
  mainMenuBtnOnClick(e) {
538
774
  estreUi.toggleMainMenuButton();
539
775
  },
@@ -542,6 +778,10 @@ const estreUi = {
542
778
  estreUi.closeMainMenu();
543
779
  },
544
780
 
781
+ overwatchPanelGrabAreaOnclick(e) {
782
+ estreUi.closeOverwatchPanel();
783
+ },
784
+
545
785
  toggleMainMenuButton() {
546
786
  if (this.isOpenMainMenu) return this.closeMainMenu();
547
787
  else return this.openMainMenu();
@@ -590,6 +830,53 @@ const estreUi = {
590
830
  },
591
831
 
592
832
 
833
+ //overwatchPanel
834
+ toggleOverwatchPanel(sectionId) {
835
+ if (this.isOpenOverwatchPanel) return this.closeOverwatchPanel();
836
+ else return this.openOverwatchPanel(sectionId);
837
+ },
838
+
839
+ openOverwatchPanel(sectionId) {
840
+ if (!this.isOpenOverwatchPanel) {
841
+ this.$overwatchPanel.attr(eds.opened, t1);
842
+ if (sectionId != null) this.showOverwatchPanelSection(sectionId);
843
+ else {
844
+ const $top = this.$panelSections.filter(asv(eds.onTop, t1));
845
+ const panelCurrentTop = $top[$top.length - 1]?.pageHandle;
846
+ if (panelCurrentTop != null) {
847
+ this.panelCurrentOnTop = panelCurrentTop;
848
+ panelCurrentTop.show(false);
849
+ }
850
+ }
851
+ return true;
852
+ } else if (sectionId != null) {
853
+ this.showOverwatchPanelSection(sectionId);
854
+ return true;
855
+ } else return false;
856
+ },
857
+
858
+ closeOverwatchPanel() {
859
+ if (this.isOpenOverwatchPanel) {
860
+ this.$overwatchPanel.attr(eds.opened, "");
861
+ this.panelCurrentOnTop?.onHide();
862
+ return true;
863
+ } else return false;
864
+ },
865
+
866
+ showOverwatchPanelSection(id) {
867
+ const $target = this.$panelSections.filter(eid + id);
868
+ if ($target.length < 1) return false;
869
+ const targetEl = $target[$target.length - 1];
870
+ targetEl.scrollIntoView({ behavior: "smooth", block: "start", inline: "start" });
871
+ const targetComponent = targetEl.pageHandle;
872
+ if (targetComponent != null) {
873
+ targetComponent.show(false);
874
+ this.panelCurrentOnTop = targetComponent;
875
+ }
876
+ return true;
877
+ },
878
+
879
+
593
880
  //rootbar
594
881
  initRootbar() {
595
882
  this.$rootTabs = this.$tabsbar.find(c.c + btn);
@@ -1393,6 +1680,45 @@ const estreUi = {
1393
1680
  return component;
1394
1681
  },
1395
1682
 
1683
+ async initStaticPanels(term = 0) {
1684
+ const $pss = this.$panelSections;
1685
+
1686
+ const delayer = (delay = term) => postPromise(resolve => setTimeout(resolve, delay));
1687
+ for (var i=0; i<$pss.length; i++) {
1688
+ this.initStaticPanel($pss[i], null, u, true);
1689
+ await delayer();
1690
+ }
1691
+
1692
+ let $top = this.$panelSections.filter(asv(eds.onTop, t1));
1693
+ if ($top.length < 1) $top = this.$panelSections.filter(eid + "quickPanel");
1694
+ if ($top.length < 1) $top = this.$panelSections;
1695
+ if ($top.length > 0) {
1696
+ const targetComponent = $top[$top.length - 1].pageHandle;
1697
+ targetComponent?.show(false);
1698
+ this.panelCurrentOnTop = targetComponent;
1699
+ }
1700
+ },
1701
+
1702
+ releaseStaticPanel(component) {
1703
+ if (component == null) return;
1704
+ const instanceId = component.instanceId;
1705
+ component.release(component.isStatic ? null : true);
1706
+ if (this.panelSections[instanceId] != null) delete this.panelSections[instanceId];
1707
+ const index = this.panelSectionList.indexOf(component);
1708
+ if (index > -1) this.panelSectionList.splice(index, 1);
1709
+ },
1710
+
1711
+ initStaticPanel(bound, intent = null, instanceOrigin, init = false) {
1712
+ this.releaseStaticPanel(bound.pageHandle);
1713
+ const component = new EstrePanelComponent(bound, instanceOrigin);
1714
+ if (!init || component.isStatic) {
1715
+ this.panelSections[component.instanceId] = component;
1716
+ this.panelSectionList.push(component);
1717
+ }
1718
+ component.init(intent);
1719
+ return component;
1720
+ },
1721
+
1396
1722
  async initHeaderBars(term = 0) {
1397
1723
  const $hss = this.$headerSections;
1398
1724