@ogidor/dashboard 1.0.11 → 1.0.13
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/README.md +77 -41
- package/app/dashboard.component.d.ts +18 -4
- package/app/models.d.ts +20 -0
- package/esm2020/app/dashboard.component.mjs +272 -70
- package/esm2020/app/models.mjs +1 -1
- package/fesm2015/ogidor-dashboard.mjs +272 -70
- package/fesm2015/ogidor-dashboard.mjs.map +1 -1
- package/fesm2020/ogidor-dashboard.mjs +271 -69
- package/fesm2020/ogidor-dashboard.mjs.map +1 -1
- package/package.json +1 -1
package/esm2020/app/models.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export {};
|
|
2
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibW9kZWxzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL2FwcC9tb2RlbHMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IiIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogQSBzaW5nbGUgd2lkZ2V0IG9uIHRoZSBncmlkLlxuICogVGhlIGxpYiBpcyBjb250ZW50LWFnbm9zdGljIOKAlCBgZGF0YWAgY2FuIGhvbGQgYW55dGhpbmcgdGhlIGNvbnN1bWVyIG5lZWRzLlxuICovXG5leHBvcnQgaW50ZXJmYWNlIFdpZGdldCB7XG4gIGlkOiBzdHJpbmc7XG4gIC8qKiBHcmlkIGNvbHVtbiAoMC1iYXNlZCkgKi9cbiAgeDogbnVtYmVyO1xuICAvKiogR3JpZCByb3cgKDAtYmFzZWQpICovXG4gIHk6IG51bWJlcjtcbiAgLyoqIE51bWJlciBvZiBjb2x1bW5zIHRoaXMgd2lkZ2V0IHNwYW5zICovXG4gIGNvbHM6IG51bWJlcjtcbiAgLyoqIE51bWJlciBvZiByb3dzIHRoaXMgd2lkZ2V0IHNwYW5zICovXG4gIHJvd3M6IG51bWJlcjtcbiAgLyoqIERpc3BsYXkgdGl0bGUgc2hvd24gaW4gdGhlIGNhcmQgaGVhZGVyICovXG4gIHRpdGxlOiBzdHJpbmc7XG4gIC8qKiBPcHRpb25hbCBwZXItd2lkZ2V0IGNhcmQgYmFja2dyb3VuZCBjb2xvci4gT3ZlcnJpZGVzIHRoZSBnbG9iYWwgdGhlbWUgd2lkZ2V0Q2FyZENvbG9yLiAqL1xuICBjYXJkQ29sb3I/
|
|
2
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibW9kZWxzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL2FwcC9tb2RlbHMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IiIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogQSBzaW5nbGUgd2lkZ2V0IG9uIHRoZSBncmlkLlxuICogVGhlIGxpYiBpcyBjb250ZW50LWFnbm9zdGljIOKAlCBgZGF0YWAgY2FuIGhvbGQgYW55dGhpbmcgdGhlIGNvbnN1bWVyIG5lZWRzLlxuICovXG5leHBvcnQgaW50ZXJmYWNlIFdpZGdldCB7XG4gIGlkOiBzdHJpbmc7XG4gIC8qKiBHcmlkIGNvbHVtbiAoMC1iYXNlZCkgKi9cbiAgeDogbnVtYmVyO1xuICAvKiogR3JpZCByb3cgKDAtYmFzZWQpICovXG4gIHk6IG51bWJlcjtcbiAgLyoqIE51bWJlciBvZiBjb2x1bW5zIHRoaXMgd2lkZ2V0IHNwYW5zICovXG4gIGNvbHM6IG51bWJlcjtcbiAgLyoqIE51bWJlciBvZiByb3dzIHRoaXMgd2lkZ2V0IHNwYW5zICovXG4gIHJvd3M6IG51bWJlcjtcbiAgLyoqIERpc3BsYXkgdGl0bGUgc2hvd24gaW4gdGhlIGNhcmQgaGVhZGVyICovXG4gIHRpdGxlOiBzdHJpbmc7XG4gIC8qKiBPcHRpb25hbCBwZXItd2lkZ2V0IGNhcmQgYmFja2dyb3VuZCBjb2xvci4gT3ZlcnJpZGVzIHRoZSBnbG9iYWwgdGhlbWUgd2lkZ2V0Q2FyZENvbG9yLiAqL1xuICBjYXJkQ29sb3I/OiBzdHJpbmc7XG4gIC8qKlxuICAgKiBBcmJpdHJhcnkgY29uc3VtZXIgZGF0YS5cbiAgICogVGhlIGxpYiBzdG9yZXMgYW5kIHN5bmNzIHRoaXMgYnV0IG5ldmVyIGluc3BlY3RzIGl0LlxuICAgKi9cbiAgZGF0YT86IGFueTtcbn1cblxuZXhwb3J0IGludGVyZmFjZSBQYWdlIHtcbiAgaWQ6IHN0cmluZztcbiAgbmFtZTogc3RyaW5nO1xuICB3aWRnZXRzOiBXaWRnZXRbXTtcbn1cblxuZXhwb3J0IGludGVyZmFjZSBEYXNoYm9hcmRDb25maWcge1xuICBwYWdlczogUGFnZVtdO1xuICBhY3RpdmVQYWdlSWQ6IHN0cmluZztcbn1cblxuLyoqXG4gKiBUaGVtZSBjb25maWd1cmF0aW9uIGZvciB0aGUgZGFzaGJvYXJkLlxuICogQWxsIHByb3BlcnRpZXMgYXJlIG9wdGlvbmFsIOKAlCBhbnkgdmFsdWVzIG5vdCBwcm92aWRlZCB3aWxsIGZhbGwgYmFjayB0byBkZWZhdWx0cy5cbiAqL1xuZXhwb3J0IGludGVyZmFjZSBEYXNoYm9hcmRUaGVtZSB7XG4gIC8qKiBCYWNrZ3JvdW5kIGNvbG9yIG9mIHRoZSBvdmVyYWxsIGRhc2hib2FyZCB3cmFwcGVyLiBEZWZhdWx0OiBgIzAwMDAwMGAgKi9cbiAgYmFja2dyb3VuZENvbG9yPzogc3RyaW5nO1xuICAvKiogQmFja2dyb3VuZCBjb2xvciBvZiB0aGUgbWFpbiBjb250ZW50IHBhbmVsLiBEZWZhdWx0OiBgIzFjMWMxZWAgKi9cbiAgcGFuZWxDb2xvcj86IHN0cmluZztcbiAgLyoqIFN1YnRsZSBib3JkZXIgb24gdGhlIG1haW4gcGFuZWwgYW5kIHBvcC1vdXQgaGVhZGVyLiBEZWZhdWx0OiBgcmdiYSgyNTUsMjU1LDI1NSwwLjA1KWAgKi9cbiAgcGFuZWxCb3JkZXJDb2xvcj86IHN0cmluZztcbiAgLyoqIEJhY2tncm91bmQgY29sb3Igb2YgaW5kaXZpZHVhbCB3aWRnZXQgY2FyZHMuIERlZmF1bHQ6IGAjMmMyYzJlYCAqL1xuICB3aWRnZXRDYXJkQ29sb3I/OiBzdHJpbmc7XG4gIC8qKiBQcmltYXJ5IGZvcmVncm91bmQvdGV4dCBjb2xvci4gRGVmYXVsdDogYCM4ZThlOTNgICovXG4gIGZvcmVDb2xvcj86IHN0cmluZztcbiAgLyoqIFByaW1hcnkgYWNjZW50IGNvbG9yIChhY3RpdmUgdGFicywgYnV0dG9ucywgZXRjLikuIERlZmF1bHQ6IGAjMGE4NGZmYCAqL1xuICBhY2NlbnRDb2xvcj86IHN0cmluZztcbiAgLyoqIExpZ2h0IHRpbnQgb2YgdGhlIGFjY2VudCBjb2xvciB1c2VkIGZvciBob3ZlciBiYWNrZ3JvdW5kcy4gRGVmYXVsdDogYHJnYmEoMTAsMTMyLDI1NSwwLjE1KWAgKi9cbiAgYWNjZW50VGludENvbG9yPzogc3RyaW5nO1xuICAvKiogU3Ryb25nZXIgdGludCBvZiB0aGUgYWNjZW50IGNvbG9yIHVzZWQgZm9yIGFjdGl2ZSBjYXJkIGJhY2tncm91bmRzLiBEZWZhdWx0OiBgcmdiYSgxMCwxMzIsMjU1LDAuMjUpYCAqL1xuICBhY2NlbnRUaW50U3Ryb25nQ29sb3I/OiBzdHJpbmc7XG4gIC8qKiBEYW5nZXIgY29sb3IgdXNlZCBvbiByZW1vdmUtd2lkZ2V0IGJ1dHRvbiBob3Zlci4gRGVmYXVsdDogYCNmZjQ1M2FgICovXG4gIGRhbmdlckNvbG9yPzogc3RyaW5nO1xuICAvKiogTGlnaHQgdGludCBvZiB0aGUgZGFuZ2VyIGNvbG9yIHVzZWQgZm9yIGhvdmVyIGJhY2tncm91bmRzLiBEZWZhdWx0OiBgcmdiYSgyNTUsNjksNTgsMC4yMilgICovXG4gIGRhbmdlclRpbnRDb2xvcj86IHN0cmluZztcbiAgLyoqIEZvbnQgZmFtaWx5IGFwcGxpZWQgdG8gdGhlIHdob2xlIGRhc2hib2FyZC4gRGVmYXVsdDogc3lzdGVtLXVpIHN0YWNrICovXG4gIGZvbnRGYW1pbHk/OiBzdHJpbmc7XG5cbiAgLy8g4pSA4pSAIEhlYWRlciAvIFRhYnMg4pSA4pSAXG4gIC8qKiBCYWNrZ3JvdW5kIG9mIHRoZSB0YWJzIHBpbGwgY29udGFpbmVyLiBEZWZhdWx0OiBgcmdiYSg0NCw0NCw0NiwwLjYpYCAqL1xuICB0YWJzQ29udGFpbmVyQ29sb3I/OiBzdHJpbmc7XG4gIC8qKiBCYWNrZ3JvdW5kIG9mIHRoZSBhY3RpdmUvc2VsZWN0ZWQgdGFiLiBEZWZhdWx0OiBgIzNhM2EzY2AgKi9cbiAgdGFiQWN0aXZlQ29sb3I/OiBzdHJpbmc7XG4gIC8qKiBUZXh0IGNvbG9yIG9mIHRoZSBhY3RpdmUgdGFiLiBEZWZhdWx0OiBgI2ZmZmZmZmAgKi9cbiAgdGFiQWN0aXZlVGV4dENvbG9yPzogc3RyaW5nO1xuICAvKiogVGV4dCBjb2xvciBvZiBob3ZlcmVkIChpbmFjdGl2ZSkgdGFicy4gRGVmYXVsdDogYCNlNWU1ZWFgICovXG4gIHRhYkhvdmVyVGV4dENvbG9yPzogc3RyaW5nO1xuICAvKiogQmFja2dyb3VuZCBvZiBhIGhvdmVyZWQgKGluYWN0aXZlKSB0YWIuIERlZmF1bHQ6IGByZ2JhKDI1NSwyNTUsMjU1LDAuMDUpYCAqL1xuICB0YWJIb3ZlckJnQ29sb3I/OiBzdHJpbmc7XG4gIC8qKiBUZXh0IGNvbG9yIG9mIHRoZSBcIkFkZCBXaWRnZXRcIiBidXR0b24uIERlZmF1bHQ6IGAjZmZmZmZmYCAqL1xuICBhZGRXaWRnZXRCdXR0b25UZXh0Q29sb3I/OiBzdHJpbmc7XG4gIC8qKiBCYWNrZ3JvdW5kIG9mIHRoZSBvdmVyZmxvdyBcIitOXCIgYnV0dG9uLiBEZWZhdWx0OiBgcmdiYSgyNTUsMjU1LDI1NSwwLjA4KWAgKi9cbiAgb3ZlcmZsb3dCdG5CZ0NvbG9yPzogc3RyaW5nO1xuICAvKiogQm9yZGVyIG9mIHRoZSBvdmVyZmxvdyBcIitOXCIgYnV0dG9uLiBEZWZhdWx0OiBgcmdiYSgyNTUsMjU1LDI1NSwwLjEyKWAgKi9cbiAgb3ZlcmZsb3dCdG5Cb3JkZXJDb2xvcj86IHN0cmluZztcblxuICAvLyDilIDilIAgVGFiLXN3aXRjaGVyIG92ZXJsYXkg4pSA4pSAXG4gIC8qKiBCYWNrZ3JvdW5kIG9mIHRoZSBmdWxsLXNjcmVlbiBvdmVybGF5IGJlaGluZCB0aGUgdGFiLXN3aXRjaGVyIHNoZWV0LiBEZWZhdWx0OiBgcmdiYSgwLDAsMCwwLjU1KWAgKi9cbiAgb3ZlcmxheUJnQ29sb3I/OiBzdHJpbmc7XG4gIC8qKiBCb3JkZXIgY29sb3Igb2YgdGhlIHRhYi1zd2l0Y2hlciBzaGVldCBhbmQgaXRzIGlubmVyIGRpdmlkZXJzLiBEZWZhdWx0OiBgcmdiYSgyNTUsMjU1LDI1NSwwLjA4KWAgKi9cbiAgc2hlZXRCb3JkZXJDb2xvcj86IHN0cmluZztcbiAgLyoqIFNjcm9sbGJhciB0aHVtYiBjb2xvciBpbnNpZGUgdGhlIHRhYi1zd2l0Y2hlciBncmlkLiBEZWZhdWx0OiBgcmdiYSgyNTUsMjU1LDI1NSwwLjE1KWAgKi9cbiAgc2Nyb2xsYmFyQ29sb3I/OiBzdHJpbmc7XG5cbiAgLy8g4pSA4pSAIFBvcC1vdXQgd2luZG93IOKUgOKUgFxuICAvKiogVGV4dCBjb2xvciBvZiB0aGUgcG9wLW91dCBwYWdlIHRpdGxlLiBEZWZhdWx0OiBgI2ZmZmZmZmAgKi9cbiAgcG9wb3V0VGl0bGVDb2xvcj86IHN0cmluZztcblxuICAvLyDilIDilIAgV2lkZ2V0IGNhcmQg4pSA4pSAXG4gIC8qKiBUZXh0IGNvbG9yIG9mIHRoZSB3aWRnZXQgY2FyZCB0aXRsZS4gRGVmYXVsdDogYCNmZmZmZmZgICovXG4gIHdpZGdldFRpdGxlQ29sb3I/OiBzdHJpbmc7XG4gIC8qKiBDb2xvciBvZiB0aGUgZHJhZy1oYW5kbGUgZG90cyBvbiBhIHdpZGdldCBjYXJkLiBEZWZhdWx0OiBgcmdiYSgyNTUsMjU1LDI1NSwwLjIpYCAqL1xuICBkcmFnSGFuZGxlQ29sb3I/OiBzdHJpbmc7XG4gIC8qKiBCb3JkZXIgY29sb3Igb2Ygd2lkZ2V0IGNhcmRzLiBEZWZhdWx0OiBgcmdiYSgyNTUsMjU1LDI1NSwwLjA3KWAgKi9cbiAgd2lkZ2V0Qm9yZGVyQ29sb3I/OiBzdHJpbmc7XG4gIC8qKiBCYWNrZ3JvdW5kIG9mIHRoZSBpY29uIGJ1dHRvbnMgKGVkaXQvcmVtb3ZlKSBvbiB3aWRnZXQgaG92ZXIuIERlZmF1bHQ6IGByZ2JhKDI1NSwyNTUsMjU1LDAuMDcpYCAqL1xuICB3aWRnZXRCdXR0b25CZ0NvbG9yPzogc3RyaW5nO1xuICAvKiogRGVmYXVsdCB0ZXh0IGNvbG9yIG9mIGljb24gYnV0dG9ucy4gRGVmYXVsdDogYHJnYmEoMjU1LDI1NSwyNTUsMC41KWAgKi9cbiAgd2lkZ2V0QnV0dG9uQ29sb3I/OiBzdHJpbmc7XG5cbiAgLy8g4pSA4pSAIEdyaWQg4pSA4pSAXG4gIC8qKiBDb2xvciBvZiB0aGUgZHJhZy9yZXNpemUgcGxhY2Vob2xkZXIgb3ZlcmxheS4gRGVmYXVsdDogc2FtZSBhcyBhY2NlbnRDb2xvciAqL1xuICBwbGFjZWhvbGRlckNvbG9yPzogc3RyaW5nO1xuICAvKiogQ29sb3Igb2YgdGhlIHJlc2l6ZSBoYW5kbGUgaWNvbi4gRGVmYXVsdDogYHJnYmEoMjU1LDI1NSwyNTUsMC4yNSlgICovXG4gIHJlc2l6ZUhhbmRsZUNvbG9yPzogc3RyaW5nO1xufVxuIl19
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { Directive, EventEmitter, TemplateRef, Component, ChangeDetectionStrategy, Input, Output, ViewChild, ContentChild, Injectable, Pipe, NgModule } from '@angular/core';
|
|
2
|
+
import { Directive, EventEmitter, TemplateRef, Component, ChangeDetectionStrategy, Input, Output, ViewChild, ContentChild, Injectable, Pipe, HostListener, NgModule } from '@angular/core';
|
|
3
3
|
import { BrowserModule } from '@angular/platform-browser';
|
|
4
4
|
import * as i2 from '@angular/common';
|
|
5
5
|
import { CommonModule } from '@angular/common';
|
|
@@ -882,16 +882,26 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImpo
|
|
|
882
882
|
const DEFAULT_THEME = {
|
|
883
883
|
backgroundColor: '#000000',
|
|
884
884
|
panelColor: '#1c1c1e',
|
|
885
|
+
panelBorderColor: 'rgba(255,255,255,0.05)',
|
|
885
886
|
widgetCardColor: '#2c2c2e',
|
|
886
887
|
foreColor: '#8e8e93',
|
|
887
888
|
accentColor: '#0a84ff',
|
|
889
|
+
accentTintColor: 'rgba(10,132,255,0.15)',
|
|
890
|
+
accentTintStrongColor: 'rgba(10,132,255,0.25)',
|
|
888
891
|
dangerColor: '#ff453a',
|
|
892
|
+
dangerTintColor: 'rgba(255,69,58,0.22)',
|
|
889
893
|
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif',
|
|
890
894
|
tabsContainerColor: 'rgba(44,44,46,0.6)',
|
|
891
895
|
tabActiveColor: '#3a3a3c',
|
|
892
896
|
tabActiveTextColor: '#ffffff',
|
|
893
897
|
tabHoverTextColor: '#e5e5ea',
|
|
898
|
+
tabHoverBgColor: 'rgba(255,255,255,0.05)',
|
|
894
899
|
addWidgetButtonTextColor: '#ffffff',
|
|
900
|
+
overflowBtnBgColor: 'rgba(255,255,255,0.08)',
|
|
901
|
+
overflowBtnBorderColor: 'rgba(255,255,255,0.12)',
|
|
902
|
+
overlayBgColor: 'rgba(0,0,0,0.55)',
|
|
903
|
+
sheetBorderColor: 'rgba(255,255,255,0.08)',
|
|
904
|
+
scrollbarColor: 'rgba(255,255,255,0.15)',
|
|
895
905
|
popoutTitleColor: '#ffffff',
|
|
896
906
|
widgetTitleColor: '#ffffff',
|
|
897
907
|
dragHandleColor: 'rgba(255,255,255,0.2)',
|
|
@@ -922,10 +932,19 @@ class DashboardComponent {
|
|
|
922
932
|
this.resolvedTheme = Object.assign({}, DEFAULT_THEME);
|
|
923
933
|
this.wrapperStyles = {};
|
|
924
934
|
this.pages = [];
|
|
935
|
+
this.visiblePages = [];
|
|
936
|
+
this.hiddenCount = 0;
|
|
925
937
|
this.activePageId = '';
|
|
926
938
|
this.isPoppedOut = false;
|
|
927
|
-
this.
|
|
939
|
+
this.tabSwitcherOpen = false;
|
|
928
940
|
this.subs = new Subscription();
|
|
941
|
+
// Reserved space for the overflow "+N" button and the add "+" button (px)
|
|
942
|
+
this.OVERFLOW_BTN_W = 80;
|
|
943
|
+
this.ADD_BTN_W = 52;
|
|
944
|
+
this.GAP = 6;
|
|
945
|
+
this.PADDING = 12; // 6px each side
|
|
946
|
+
// Cache of measured tab widths (including gap) indexed by page position
|
|
947
|
+
this._tabWidthCache = [];
|
|
929
948
|
}
|
|
930
949
|
ngOnChanges(changes) {
|
|
931
950
|
if (changes['theme'] || changes['initialLayout'])
|
|
@@ -947,11 +966,15 @@ class DashboardComponent {
|
|
|
947
966
|
this.isPoppedOut = true;
|
|
948
967
|
this.subs.add(this.stateService.pages$.subscribe(pages => {
|
|
949
968
|
this.pages = pages;
|
|
969
|
+
this._tabWidthCache = []; // invalidate cache — page list changed
|
|
950
970
|
if (this.isPoppedOut && hash) {
|
|
951
971
|
if (pages.find(p => p.id === hash))
|
|
952
972
|
this.stateService.setActivePage(hash);
|
|
953
973
|
}
|
|
954
974
|
this.updateActivePage();
|
|
975
|
+
this.recalcVisibleTabs();
|
|
976
|
+
// Second pass after Angular has rendered the updated tab elements
|
|
977
|
+
setTimeout(() => this.recalcVisibleTabs());
|
|
955
978
|
}));
|
|
956
979
|
this.subs.add(this.stateService.activePageId$.subscribe(id => {
|
|
957
980
|
this.activePageId = id;
|
|
@@ -959,18 +982,16 @@ class DashboardComponent {
|
|
|
959
982
|
}));
|
|
960
983
|
}
|
|
961
984
|
ngAfterViewInit() {
|
|
962
|
-
if (!this.
|
|
985
|
+
if (!this.tabsTrackRef)
|
|
963
986
|
return;
|
|
964
987
|
this.zone.runOutsideAngular(() => {
|
|
965
988
|
this.resizeObserver = new ResizeObserver(() => {
|
|
966
|
-
this.zone.run(() => this.
|
|
989
|
+
this.zone.run(() => this.recalcVisibleTabs());
|
|
967
990
|
});
|
|
968
|
-
this.resizeObserver.observe(this.
|
|
969
|
-
});
|
|
970
|
-
// Also detect scroll so fade hides when scrolled to end
|
|
971
|
-
this.tabsContainerRef.nativeElement.addEventListener('scroll', () => {
|
|
972
|
-
this.zone.run(() => this.checkOverflow());
|
|
991
|
+
this.resizeObserver.observe(this.tabsTrackRef.nativeElement);
|
|
973
992
|
});
|
|
993
|
+
// Initial measurement pass now that the DOM is ready
|
|
994
|
+
setTimeout(() => this.recalcVisibleTabs());
|
|
974
995
|
}
|
|
975
996
|
ngOnDestroy() {
|
|
976
997
|
var _a;
|
|
@@ -997,13 +1018,83 @@ class DashboardComponent {
|
|
|
997
1018
|
this.stateService.popOutPage(pageId);
|
|
998
1019
|
}
|
|
999
1020
|
serializeLayout() { return this.stateService.serializeLayout(); }
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1021
|
+
openTabSwitcher() { this.tabSwitcherOpen = true; }
|
|
1022
|
+
closeTabSwitcher() { this.tabSwitcherOpen = false; }
|
|
1023
|
+
onEscapeKey() { this.closeTabSwitcher(); }
|
|
1024
|
+
onSwitcherSelect(pageId) {
|
|
1025
|
+
this.stateService.setActivePage(pageId);
|
|
1026
|
+
this.closeTabSwitcher();
|
|
1027
|
+
}
|
|
1028
|
+
onSwitcherPopOut(event, pageId) {
|
|
1029
|
+
event.stopPropagation();
|
|
1030
|
+
this.stateService.popOutPage(pageId);
|
|
1031
|
+
this.closeTabSwitcher();
|
|
1032
|
+
}
|
|
1033
|
+
onSwitcherRemove(event, pageId) {
|
|
1034
|
+
event.stopPropagation();
|
|
1035
|
+
if (confirm('Remove this workspace?')) {
|
|
1036
|
+
this.stateService.removePage(pageId);
|
|
1037
|
+
if (this.pages.length <= 1)
|
|
1038
|
+
this.closeTabSwitcher();
|
|
1039
|
+
}
|
|
1040
|
+
}
|
|
1041
|
+
onSwitcherAdd() {
|
|
1042
|
+
const name = prompt('Workspace name:', `Workspace ${this.pages.length + 1}`);
|
|
1043
|
+
if (name)
|
|
1044
|
+
this.stateService.addPage(name);
|
|
1045
|
+
}
|
|
1046
|
+
recalcVisibleTabs() {
|
|
1047
|
+
if (!this.tabsTrackRef || !this.pages.length) {
|
|
1048
|
+
this.visiblePages = this.pages;
|
|
1049
|
+
this.hiddenCount = 0;
|
|
1050
|
+
this.cdr.markForCheck();
|
|
1004
1051
|
return;
|
|
1005
|
-
|
|
1006
|
-
|
|
1052
|
+
}
|
|
1053
|
+
const track = this.tabsTrackRef.nativeElement;
|
|
1054
|
+
const trackW = track.clientWidth;
|
|
1055
|
+
// Space always consumed by the add "+" button + padding
|
|
1056
|
+
const baseReserved = this.ADD_BTN_W + this.GAP + this.PADDING;
|
|
1057
|
+
// Space needed for the overflow "+N" button when shown
|
|
1058
|
+
const overflowSlot = this.OVERFLOW_BTN_W + this.GAP;
|
|
1059
|
+
// Measure each real tab element currently in the DOM.
|
|
1060
|
+
// The DOM always renders all visiblePages tabs, so we may not have all tabs
|
|
1061
|
+
// measured yet — fall back to the previous measured cache or a generous estimate.
|
|
1062
|
+
const tabEls = Array.from(track.querySelectorAll('.tab'));
|
|
1063
|
+
// Build a width map by index (only covers currently visible tabs)
|
|
1064
|
+
const measuredWidths = this.pages.map((_, i) => {
|
|
1065
|
+
var _a;
|
|
1066
|
+
const el = tabEls[i];
|
|
1067
|
+
return el ? el.getBoundingClientRect().width + this.GAP : (_a = this._tabWidthCache[i]) !== null && _a !== void 0 ? _a : 150 + this.GAP;
|
|
1068
|
+
});
|
|
1069
|
+
// Update cache for next call
|
|
1070
|
+
tabEls.forEach((el, i) => {
|
|
1071
|
+
this._tabWidthCache[i] = el.getBoundingClientRect().width + this.GAP;
|
|
1072
|
+
});
|
|
1073
|
+
// How much space the tabs need in total (no overflow button)
|
|
1074
|
+
const totalNeeded = measuredWidths.reduce((s, w) => s + w, 0) - this.GAP; // remove trailing gap
|
|
1075
|
+
if (totalNeeded <= trackW - baseReserved) {
|
|
1076
|
+
// Everything fits — show all
|
|
1077
|
+
this.visiblePages = this.pages;
|
|
1078
|
+
this.hiddenCount = 0;
|
|
1079
|
+
}
|
|
1080
|
+
else {
|
|
1081
|
+
// Not everything fits — fill as many as possible while always leaving room for overflow btn
|
|
1082
|
+
const budget = trackW - baseReserved - overflowSlot;
|
|
1083
|
+
let used = 0;
|
|
1084
|
+
let count = 0;
|
|
1085
|
+
for (let i = 0; i < this.pages.length; i++) {
|
|
1086
|
+
used += measuredWidths[i];
|
|
1087
|
+
if (used - this.GAP <= budget) { // -GAP: last item has no trailing gap
|
|
1088
|
+
count++;
|
|
1089
|
+
}
|
|
1090
|
+
else {
|
|
1091
|
+
break;
|
|
1092
|
+
}
|
|
1093
|
+
}
|
|
1094
|
+
count = Math.max(1, count);
|
|
1095
|
+
this.visiblePages = this.pages.slice(0, count);
|
|
1096
|
+
this.hiddenCount = this.pages.length - count;
|
|
1097
|
+
}
|
|
1007
1098
|
this.cdr.markForCheck();
|
|
1008
1099
|
}
|
|
1009
1100
|
applyTheme() {
|
|
@@ -1013,16 +1104,26 @@ class DashboardComponent {
|
|
|
1013
1104
|
this.wrapperStyles = {
|
|
1014
1105
|
'--dash-bg': t.backgroundColor,
|
|
1015
1106
|
'--dash-panel-bg': t.panelColor,
|
|
1107
|
+
'--dash-panel-border': t.panelBorderColor,
|
|
1016
1108
|
'--dash-card-bg': t.widgetCardColor,
|
|
1017
1109
|
'--dash-fore-color': t.foreColor,
|
|
1018
1110
|
'--dash-accent-color': t.accentColor,
|
|
1111
|
+
'--dash-accent-tint': t.accentTintColor,
|
|
1112
|
+
'--dash-accent-tint-strong': t.accentTintStrongColor,
|
|
1019
1113
|
'--dash-danger-color': t.dangerColor,
|
|
1114
|
+
'--dash-danger-tint': t.dangerTintColor,
|
|
1020
1115
|
'--dash-font-family': t.fontFamily,
|
|
1021
1116
|
'--dash-tabs-container-color': t.tabsContainerColor,
|
|
1022
1117
|
'--dash-tab-active-color': t.tabActiveColor,
|
|
1023
1118
|
'--dash-tab-active-text': t.tabActiveTextColor,
|
|
1024
1119
|
'--dash-tab-hover-text': t.tabHoverTextColor,
|
|
1120
|
+
'--dash-tab-hover-bg': t.tabHoverBgColor,
|
|
1025
1121
|
'--dash-add-widget-text': t.addWidgetButtonTextColor,
|
|
1122
|
+
'--dash-overflow-btn-bg': t.overflowBtnBgColor,
|
|
1123
|
+
'--dash-overflow-btn-border': t.overflowBtnBorderColor,
|
|
1124
|
+
'--dash-overlay-bg': t.overlayBgColor,
|
|
1125
|
+
'--dash-sheet-border': t.sheetBorderColor,
|
|
1126
|
+
'--dash-scrollbar-color': t.scrollbarColor,
|
|
1026
1127
|
'--dash-popout-title-color': t.popoutTitleColor,
|
|
1027
1128
|
'--dash-widget-title-color': t.widgetTitleColor,
|
|
1028
1129
|
'--dash-drag-handle-color': t.dragHandleColor,
|
|
@@ -1035,18 +1136,10 @@ class DashboardComponent {
|
|
|
1035
1136
|
}
|
|
1036
1137
|
updateActivePage() {
|
|
1037
1138
|
this.activePage = this.pages.find(p => p.id === this.activePageId);
|
|
1038
|
-
// scroll active tab into view
|
|
1039
|
-
setTimeout(() => {
|
|
1040
|
-
if (!this.tabsContainerRef)
|
|
1041
|
-
return;
|
|
1042
|
-
const active = this.tabsContainerRef.nativeElement.querySelector('.tab.active');
|
|
1043
|
-
active === null || active === void 0 ? void 0 : active.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'nearest' });
|
|
1044
|
-
this.checkOverflow();
|
|
1045
|
-
});
|
|
1046
1139
|
}
|
|
1047
1140
|
}
|
|
1048
1141
|
DashboardComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: DashboardComponent, deps: [{ token: DashboardStateService }, { token: i0.ChangeDetectorRef }, { token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Component });
|
|
1049
|
-
DashboardComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.10", type: DashboardComponent, selector: "app-dashboard", inputs: { initialLayout: "initialLayout", theme: "theme" }, outputs: { addWidgetRequested: "addWidgetRequested", editWidgetRequested: "editWidgetRequested" }, queries: [{ propertyName: "cellTemplate", first: true, predicate: GridCellDirective, descendants: true, read: TemplateRef }], viewQueries: [{ propertyName: "
|
|
1142
|
+
DashboardComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.10", type: DashboardComponent, selector: "app-dashboard", inputs: { initialLayout: "initialLayout", theme: "theme" }, outputs: { addWidgetRequested: "addWidgetRequested", editWidgetRequested: "editWidgetRequested" }, host: { listeners: { "document:keydown.escape": "onEscapeKey()" } }, queries: [{ propertyName: "cellTemplate", first: true, predicate: GridCellDirective, descendants: true, read: TemplateRef }], viewQueries: [{ propertyName: "tabsTrackRef", first: true, predicate: ["tabsTrack"], descendants: true }], usesOnChanges: true, ngImport: i0, template: `
|
|
1050
1143
|
<!-- POPPED-OUT MODE -->
|
|
1051
1144
|
<ng-container *ngIf="isPoppedOut; else normalMode">
|
|
1052
1145
|
<div class="popout-wrapper" [ngStyle]="wrapperStyles">
|
|
@@ -1075,29 +1168,39 @@ DashboardComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", ver
|
|
|
1075
1168
|
|
|
1076
1169
|
<header class="dashboard-header">
|
|
1077
1170
|
|
|
1078
|
-
<!--
|
|
1079
|
-
<div class="tabs-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
</button>
|
|
1094
|
-
</div>
|
|
1095
|
-
<button class="btn-add-page" (click)="onAddPage()" title="New workspace">
|
|
1096
|
-
<i class="la la-plus"></i>
|
|
1171
|
+
<!-- Tab bar -->
|
|
1172
|
+
<div class="tabs-track" #tabsTrack>
|
|
1173
|
+
<!-- Visible tabs -->
|
|
1174
|
+
<div
|
|
1175
|
+
*ngFor="let page of visiblePages"
|
|
1176
|
+
class="tab"
|
|
1177
|
+
[class.active]="page.id === activePageId"
|
|
1178
|
+
(click)="onSelectPage(page.id)"
|
|
1179
|
+
>
|
|
1180
|
+
<span class="tab-name">{{ page.name }}</span>
|
|
1181
|
+
<button class="tab-action tab-popout" (click)="onPopOut($event, page.id)" title="Open in new window">
|
|
1182
|
+
<i class="la la-external-link-alt"></i>
|
|
1183
|
+
</button>
|
|
1184
|
+
<button class="tab-action tab-close" *ngIf="pages.length > 1" (click)="onRemovePage($event, page.id)" title="Close">
|
|
1185
|
+
<i class="la la-times"></i>
|
|
1097
1186
|
</button>
|
|
1098
1187
|
</div>
|
|
1099
|
-
|
|
1100
|
-
|
|
1188
|
+
|
|
1189
|
+
<!-- Overflow button — shown only when there are hidden tabs -->
|
|
1190
|
+
<button
|
|
1191
|
+
*ngIf="hiddenCount > 0"
|
|
1192
|
+
class="btn-overflow"
|
|
1193
|
+
(click)="openTabSwitcher()"
|
|
1194
|
+
title="Show all workspaces"
|
|
1195
|
+
>
|
|
1196
|
+
<span>+{{ hiddenCount }}</span>
|
|
1197
|
+
<i class="la la-th-large"></i>
|
|
1198
|
+
</button>
|
|
1199
|
+
|
|
1200
|
+
<!-- Add page button -->
|
|
1201
|
+
<button class="btn-add-page" (click)="onAddPage()" title="New workspace">
|
|
1202
|
+
<i class="la la-plus"></i>
|
|
1203
|
+
</button>
|
|
1101
1204
|
</div>
|
|
1102
1205
|
|
|
1103
1206
|
<button class="btn-add-widget" (click)="addWidgetRequested.emit()" title="Add widget">
|
|
@@ -1107,6 +1210,49 @@ DashboardComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", ver
|
|
|
1107
1210
|
|
|
1108
1211
|
</header>
|
|
1109
1212
|
|
|
1213
|
+
<!-- ── Tab-switcher overlay (Chrome mobile style) ── -->
|
|
1214
|
+
<div class="tab-switcher-overlay" *ngIf="tabSwitcherOpen" (click)="closeTabSwitcher()">
|
|
1215
|
+
<div class="tab-switcher-sheet" (click)="$event.stopPropagation()">
|
|
1216
|
+
|
|
1217
|
+
<div class="tab-switcher-header">
|
|
1218
|
+
<span class="tab-switcher-title">{{ pages.length }} Workspace{{ pages.length !== 1 ? 's' : '' }}</span>
|
|
1219
|
+
<button class="tab-switcher-close-btn" (click)="closeTabSwitcher()" title="Close">
|
|
1220
|
+
<i class="la la-times"></i>
|
|
1221
|
+
</button>
|
|
1222
|
+
</div>
|
|
1223
|
+
|
|
1224
|
+
<div class="tab-switcher-grid">
|
|
1225
|
+
<div
|
|
1226
|
+
*ngFor="let page of pages"
|
|
1227
|
+
class="tab-card"
|
|
1228
|
+
[class.active]="page.id === activePageId"
|
|
1229
|
+
(click)="onSwitcherSelect(page.id)"
|
|
1230
|
+
>
|
|
1231
|
+
<div class="tab-card-icon">
|
|
1232
|
+
<i class="la la-th-large"></i>
|
|
1233
|
+
</div>
|
|
1234
|
+
<span class="tab-card-name">{{ page.name }}</span>
|
|
1235
|
+
<div class="tab-card-actions">
|
|
1236
|
+
<button class="tab-card-btn tab-card-popout" (click)="onSwitcherPopOut($event, page.id)" title="Open in new window">
|
|
1237
|
+
<i class="la la-external-link-alt"></i>
|
|
1238
|
+
</button>
|
|
1239
|
+
<button class="tab-card-btn tab-card-remove" *ngIf="pages.length > 1" (click)="onSwitcherRemove($event, page.id)" title="Close">
|
|
1240
|
+
<i class="la la-times"></i>
|
|
1241
|
+
</button>
|
|
1242
|
+
</div>
|
|
1243
|
+
</div>
|
|
1244
|
+
</div>
|
|
1245
|
+
|
|
1246
|
+
<div class="tab-switcher-footer">
|
|
1247
|
+
<button class="tab-switcher-add-btn" (click)="onSwitcherAdd()">
|
|
1248
|
+
<i class="la la-plus"></i>
|
|
1249
|
+
<span>New Workspace</span>
|
|
1250
|
+
</button>
|
|
1251
|
+
</div>
|
|
1252
|
+
|
|
1253
|
+
</div>
|
|
1254
|
+
</div>
|
|
1255
|
+
|
|
1110
1256
|
<div class="grid-container">
|
|
1111
1257
|
<app-grid [widgets]="activePage?.widgets || []" (itemChanged)="onItemChanged($event)">
|
|
1112
1258
|
<ng-template gridCell let-widget="widget">
|
|
@@ -1125,7 +1271,7 @@ DashboardComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", ver
|
|
|
1125
1271
|
</main>
|
|
1126
1272
|
</div>
|
|
1127
1273
|
</ng-template>
|
|
1128
|
-
`, isInline: true, styles: [":host{display:flex;flex-direction:column;width:100%;height:100%;min-height:0;--dash-bg: #000000;--dash-panel-bg: #1c1c1e;--dash-card-bg: #2c2c2e;--dash-fore-color: #8e8e93;--dash-accent-color: #0a84ff;--dash-danger-color: #ff453a;--dash-font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif;--dash-tabs-container-color: rgba(44,44,46,.6);--dash-tab-active-color: #3a3a3c;--dash-tab-active-text: #ffffff;--dash-tab-hover-text: #e5e5ea;--dash-add-widget-text: #ffffff;--dash-popout-title-color: #ffffff;--dash-widget-title-color: #ffffff;--dash-drag-handle-color: rgba(255,255,255,.2);--dash-widget-border-color: rgba(255,255,255,.07);--dash-widget-btn-bg: rgba(255,255,255,.07);--dash-widget-btn-color: rgba(255,255,255,.5);--dash-placeholder-color: #0a84ff;--dash-resize-handle-color: rgba(255,255,255,.25)}.dashboard-wrapper{display:flex;flex:1;min-height:0;overflow:hidden;padding:16px;box-sizing:border-box;background:var(--dash-bg);color:var(--dash-fore-color);font-family:var(--dash-font-family)}.main-content{flex:1;display:flex;flex-direction:column;overflow:hidden;border-radius:40px;padding:20px;background:var(--dash-panel-bg);border:1px solid
|
|
1274
|
+
`, isInline: true, styles: [":host{display:flex;flex-direction:column;width:100%;height:100%;min-height:0;--dash-bg: #000000;--dash-panel-bg: #1c1c1e;--dash-panel-border: rgba(255,255,255,.05);--dash-card-bg: #2c2c2e;--dash-fore-color: #8e8e93;--dash-accent-color: #0a84ff;--dash-accent-tint: rgba(10,132,255,.15);--dash-accent-tint-strong: rgba(10,132,255,.25);--dash-danger-color: #ff453a;--dash-danger-tint: rgba(255,69,58,.22);--dash-font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif;--dash-tabs-container-color: rgba(44,44,46,.6);--dash-tab-active-color: #3a3a3c;--dash-tab-active-text: #ffffff;--dash-tab-hover-text: #e5e5ea;--dash-tab-hover-bg: rgba(255,255,255,.05);--dash-add-widget-text: #ffffff;--dash-overflow-btn-bg: rgba(255,255,255,.08);--dash-overflow-btn-border: rgba(255,255,255,.12);--dash-overlay-bg: rgba(0,0,0,.55);--dash-sheet-border: rgba(255,255,255,.08);--dash-scrollbar-color: rgba(255,255,255,.15);--dash-popout-title-color: #ffffff;--dash-widget-title-color: #ffffff;--dash-drag-handle-color: rgba(255,255,255,.2);--dash-widget-border-color: rgba(255,255,255,.07);--dash-widget-btn-bg: rgba(255,255,255,.07);--dash-widget-btn-color: rgba(255,255,255,.5);--dash-placeholder-color: #0a84ff;--dash-resize-handle-color: rgba(255,255,255,.25)}.dashboard-wrapper{display:flex;flex:1;min-height:0;overflow:hidden;padding:16px;box-sizing:border-box;background:var(--dash-bg);color:var(--dash-fore-color);font-family:var(--dash-font-family)}.main-content{flex:1;display:flex;flex-direction:column;overflow:hidden;border-radius:40px;padding:20px;background:var(--dash-panel-bg);border:1px solid var(--dash-panel-border);position:relative}.popout-wrapper{display:flex;flex:1;flex-direction:column;min-height:0;overflow:hidden;padding:16px;box-sizing:border-box;background:var(--dash-bg);color:var(--dash-fore-color);font-family:var(--dash-font-family)}.popout-header{display:flex;align-items:center;margin-bottom:16px;padding:10px 18px;background:var(--dash-panel-bg);border-radius:20px;border:1px solid var(--dash-panel-border);flex-shrink:0}.popout-title{font-size:16px;font-weight:700;color:var(--dash-popout-title-color)}.popout-wrapper app-grid{flex:1;min-height:0;overflow:auto}.dashboard-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:20px;gap:12px;flex-shrink:0}.tabs-track{display:flex;align-items:center;gap:6px;flex:1;min-width:0;background:var(--dash-tabs-container-color);backdrop-filter:blur(10px);border-radius:25px;padding:6px;overflow:hidden}.tab{display:flex;align-items:center;gap:4px;padding:8px 14px 8px 18px;border-radius:20px;cursor:pointer;font-size:14px;font-weight:500;color:var(--dash-fore-color);white-space:nowrap;flex-shrink:0;transition:background .2s,color .2s}.tab.active{background:var(--dash-tab-active-color);color:var(--dash-tab-active-text)}.tab:hover:not(.active){color:var(--dash-tab-hover-text);background:var(--dash-tab-hover-bg)}.tab-name{white-space:nowrap}.tab-action{background:transparent;border:none;color:var(--dash-fore-color);padding:2px;font-size:11px;cursor:pointer;opacity:0;border-radius:50%;width:20px;height:20px;display:flex;align-items:center;justify-content:center;transition:all .2s;flex-shrink:0}.tab:hover .tab-action{opacity:1}.tab-popout:hover{color:var(--dash-accent-color);background:var(--dash-accent-tint)}.tab-close:hover{color:var(--dash-danger-color);background:var(--dash-danger-tint)}.btn-overflow{display:inline-flex;align-items:center;gap:5px;background:var(--dash-overflow-btn-bg);border:1px solid var(--dash-overflow-btn-border);border-radius:20px;padding:7px 13px;cursor:pointer;font-size:13px;font-weight:600;color:var(--dash-tab-active-text);white-space:nowrap;flex-shrink:0;transition:background .2s,border-color .2s,color .2s}.btn-overflow:hover{background:var(--dash-accent-tint);border-color:var(--dash-accent-color);color:var(--dash-accent-color)}.btn-add-page{background:transparent;border:none;color:var(--dash-fore-color);padding:8px 14px;cursor:pointer;border-radius:20px;flex-shrink:0;transition:all .2s}.btn-add-page:hover{color:var(--dash-accent-color);background:var(--dash-accent-tint)}.tab-switcher-overlay{position:absolute;inset:0;z-index:1000;background:var(--dash-overlay-bg);backdrop-filter:blur(4px);display:flex;align-items:flex-start;justify-content:center;padding-top:60px;animation:overlay-in .18s ease}@keyframes overlay-in{0%{opacity:0}to{opacity:1}}.tab-switcher-sheet{background:var(--dash-panel-bg);border:1px solid var(--dash-sheet-border);border-radius:24px;width:min(520px,calc(100% - 32px));max-height:calc(100% - 80px);display:flex;flex-direction:column;overflow:hidden;animation:sheet-in .22s cubic-bezier(.32,1.2,.5,1)}@keyframes sheet-in{0%{transform:translateY(-16px) scale(.97);opacity:0}to{transform:translateY(0) scale(1);opacity:1}}.tab-switcher-header{display:flex;align-items:center;justify-content:space-between;padding:16px 20px 12px;border-bottom:1px solid var(--dash-sheet-border);flex-shrink:0}.tab-switcher-title{font-size:15px;font-weight:700;color:var(--dash-tab-active-text)}.tab-switcher-close-btn{background:var(--dash-widget-btn-bg);border:none;color:var(--dash-fore-color);width:28px;height:28px;border-radius:50%;display:flex;align-items:center;justify-content:center;cursor:pointer;font-size:14px;transition:background .15s,color .15s}.tab-switcher-close-btn:hover{background:var(--dash-danger-tint);color:var(--dash-danger-color)}.tab-switcher-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(140px,1fr));gap:10px;padding:16px;overflow-y:auto;scrollbar-width:thin;scrollbar-color:var(--dash-scrollbar-color) transparent}.tab-card{display:flex;flex-direction:column;align-items:center;gap:8px;padding:16px 12px 12px;border-radius:16px;background:var(--dash-card-bg);border:1.5px solid transparent;cursor:pointer;transition:border-color .15s,background .15s,transform .12s;position:relative}.tab-card:hover{background:var(--dash-tab-hover-bg);transform:translateY(-2px)}.tab-card.active{border-color:var(--dash-accent-color);background:var(--dash-accent-tint)}.tab-card-icon{width:44px;height:44px;border-radius:12px;background:var(--dash-widget-btn-bg);display:flex;align-items:center;justify-content:center;font-size:20px;color:var(--dash-fore-color)}.tab-card.active .tab-card-icon{background:var(--dash-accent-tint-strong);color:var(--dash-accent-color)}.tab-card-name{font-size:13px;font-weight:500;color:var(--dash-tab-active-text);text-align:center;word-break:break-word;line-height:1.3}.tab-card-actions{display:flex;gap:4px;opacity:0;transition:opacity .15s}.tab-card:hover .tab-card-actions{opacity:1}.tab-card-btn{background:var(--dash-widget-btn-bg);border:none;width:26px;height:26px;border-radius:50%;display:flex;align-items:center;justify-content:center;cursor:pointer;font-size:12px;color:var(--dash-fore-color);transition:background .15s,color .15s}.tab-card-popout:hover{background:var(--dash-accent-tint-strong);color:var(--dash-accent-color)}.tab-card-remove:hover{background:var(--dash-danger-tint);color:var(--dash-danger-color)}.tab-switcher-footer{padding:12px 16px 16px;border-top:1px solid var(--dash-sheet-border);flex-shrink:0}.tab-switcher-add-btn{display:flex;align-items:center;justify-content:center;gap:8px;width:100%;padding:11px 20px;border-radius:16px;background:var(--dash-accent-tint);border:1.5px dashed var(--dash-accent-tint-strong);color:var(--dash-accent-color);font-size:14px;font-weight:600;cursor:pointer;transition:background .15s,border-color .15s}.tab-switcher-add-btn:hover{background:var(--dash-accent-tint-strong);border-color:var(--dash-accent-color)}.btn-add-widget{display:flex;align-items:center;gap:6px;background:var(--dash-accent-color);border:none;color:var(--dash-add-widget-text);font-size:14px;font-weight:600;padding:10px 20px;border-radius:22px;cursor:pointer;white-space:nowrap;flex-shrink:0;transition:opacity .2s,transform .15s}.btn-add-widget:hover{opacity:.9;transform:translateY(-1px)}.btn-add-widget:active{transform:translateY(0)}.main-content .grid-container{flex:1;overflow:auto;border-radius:24px;min-height:0}app-grid{background:transparent;display:block;height:100%;width:100%}\n"], dependencies: [{ kind: "directive", type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i2.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "component", type: WidgetRendererComponent, selector: "app-widget-renderer", inputs: ["widget", "theme"], outputs: ["editRequested", "removeRequested"] }, { kind: "component", type: CustomGridComponent, selector: "app-grid", inputs: ["widgets", "columns", "gap", "rowHeight", "minItemCols", "minItemRows"], outputs: ["itemChanged", "layoutChanged"] }, { kind: "directive", type: GridCellDirective, selector: "[gridCell]" }] });
|
|
1129
1275
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: DashboardComponent, decorators: [{
|
|
1130
1276
|
type: Component,
|
|
1131
1277
|
args: [{ selector: 'app-dashboard', template: `
|
|
@@ -1157,29 +1303,39 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImpo
|
|
|
1157
1303
|
|
|
1158
1304
|
<header class="dashboard-header">
|
|
1159
1305
|
|
|
1160
|
-
<!--
|
|
1161
|
-
<div class="tabs-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
</button>
|
|
1176
|
-
</div>
|
|
1177
|
-
<button class="btn-add-page" (click)="onAddPage()" title="New workspace">
|
|
1178
|
-
<i class="la la-plus"></i>
|
|
1306
|
+
<!-- Tab bar -->
|
|
1307
|
+
<div class="tabs-track" #tabsTrack>
|
|
1308
|
+
<!-- Visible tabs -->
|
|
1309
|
+
<div
|
|
1310
|
+
*ngFor="let page of visiblePages"
|
|
1311
|
+
class="tab"
|
|
1312
|
+
[class.active]="page.id === activePageId"
|
|
1313
|
+
(click)="onSelectPage(page.id)"
|
|
1314
|
+
>
|
|
1315
|
+
<span class="tab-name">{{ page.name }}</span>
|
|
1316
|
+
<button class="tab-action tab-popout" (click)="onPopOut($event, page.id)" title="Open in new window">
|
|
1317
|
+
<i class="la la-external-link-alt"></i>
|
|
1318
|
+
</button>
|
|
1319
|
+
<button class="tab-action tab-close" *ngIf="pages.length > 1" (click)="onRemovePage($event, page.id)" title="Close">
|
|
1320
|
+
<i class="la la-times"></i>
|
|
1179
1321
|
</button>
|
|
1180
1322
|
</div>
|
|
1181
|
-
|
|
1182
|
-
|
|
1323
|
+
|
|
1324
|
+
<!-- Overflow button — shown only when there are hidden tabs -->
|
|
1325
|
+
<button
|
|
1326
|
+
*ngIf="hiddenCount > 0"
|
|
1327
|
+
class="btn-overflow"
|
|
1328
|
+
(click)="openTabSwitcher()"
|
|
1329
|
+
title="Show all workspaces"
|
|
1330
|
+
>
|
|
1331
|
+
<span>+{{ hiddenCount }}</span>
|
|
1332
|
+
<i class="la la-th-large"></i>
|
|
1333
|
+
</button>
|
|
1334
|
+
|
|
1335
|
+
<!-- Add page button -->
|
|
1336
|
+
<button class="btn-add-page" (click)="onAddPage()" title="New workspace">
|
|
1337
|
+
<i class="la la-plus"></i>
|
|
1338
|
+
</button>
|
|
1183
1339
|
</div>
|
|
1184
1340
|
|
|
1185
1341
|
<button class="btn-add-widget" (click)="addWidgetRequested.emit()" title="Add widget">
|
|
@@ -1189,6 +1345,49 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImpo
|
|
|
1189
1345
|
|
|
1190
1346
|
</header>
|
|
1191
1347
|
|
|
1348
|
+
<!-- ── Tab-switcher overlay (Chrome mobile style) ── -->
|
|
1349
|
+
<div class="tab-switcher-overlay" *ngIf="tabSwitcherOpen" (click)="closeTabSwitcher()">
|
|
1350
|
+
<div class="tab-switcher-sheet" (click)="$event.stopPropagation()">
|
|
1351
|
+
|
|
1352
|
+
<div class="tab-switcher-header">
|
|
1353
|
+
<span class="tab-switcher-title">{{ pages.length }} Workspace{{ pages.length !== 1 ? 's' : '' }}</span>
|
|
1354
|
+
<button class="tab-switcher-close-btn" (click)="closeTabSwitcher()" title="Close">
|
|
1355
|
+
<i class="la la-times"></i>
|
|
1356
|
+
</button>
|
|
1357
|
+
</div>
|
|
1358
|
+
|
|
1359
|
+
<div class="tab-switcher-grid">
|
|
1360
|
+
<div
|
|
1361
|
+
*ngFor="let page of pages"
|
|
1362
|
+
class="tab-card"
|
|
1363
|
+
[class.active]="page.id === activePageId"
|
|
1364
|
+
(click)="onSwitcherSelect(page.id)"
|
|
1365
|
+
>
|
|
1366
|
+
<div class="tab-card-icon">
|
|
1367
|
+
<i class="la la-th-large"></i>
|
|
1368
|
+
</div>
|
|
1369
|
+
<span class="tab-card-name">{{ page.name }}</span>
|
|
1370
|
+
<div class="tab-card-actions">
|
|
1371
|
+
<button class="tab-card-btn tab-card-popout" (click)="onSwitcherPopOut($event, page.id)" title="Open in new window">
|
|
1372
|
+
<i class="la la-external-link-alt"></i>
|
|
1373
|
+
</button>
|
|
1374
|
+
<button class="tab-card-btn tab-card-remove" *ngIf="pages.length > 1" (click)="onSwitcherRemove($event, page.id)" title="Close">
|
|
1375
|
+
<i class="la la-times"></i>
|
|
1376
|
+
</button>
|
|
1377
|
+
</div>
|
|
1378
|
+
</div>
|
|
1379
|
+
</div>
|
|
1380
|
+
|
|
1381
|
+
<div class="tab-switcher-footer">
|
|
1382
|
+
<button class="tab-switcher-add-btn" (click)="onSwitcherAdd()">
|
|
1383
|
+
<i class="la la-plus"></i>
|
|
1384
|
+
<span>New Workspace</span>
|
|
1385
|
+
</button>
|
|
1386
|
+
</div>
|
|
1387
|
+
|
|
1388
|
+
</div>
|
|
1389
|
+
</div>
|
|
1390
|
+
|
|
1192
1391
|
<div class="grid-container">
|
|
1193
1392
|
<app-grid [widgets]="activePage?.widgets || []" (itemChanged)="onItemChanged($event)">
|
|
1194
1393
|
<ng-template gridCell let-widget="widget">
|
|
@@ -1207,7 +1406,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImpo
|
|
|
1207
1406
|
</main>
|
|
1208
1407
|
</div>
|
|
1209
1408
|
</ng-template>
|
|
1210
|
-
`, styles: [":host{display:flex;flex-direction:column;width:100%;height:100%;min-height:0;--dash-bg: #000000;--dash-panel-bg: #1c1c1e;--dash-card-bg: #2c2c2e;--dash-fore-color: #8e8e93;--dash-accent-color: #0a84ff;--dash-danger-color: #ff453a;--dash-font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif;--dash-tabs-container-color: rgba(44,44,46,.6);--dash-tab-active-color: #3a3a3c;--dash-tab-active-text: #ffffff;--dash-tab-hover-text: #e5e5ea;--dash-add-widget-text: #ffffff;--dash-popout-title-color: #ffffff;--dash-widget-title-color: #ffffff;--dash-drag-handle-color: rgba(255,255,255,.2);--dash-widget-border-color: rgba(255,255,255,.07);--dash-widget-btn-bg: rgba(255,255,255,.07);--dash-widget-btn-color: rgba(255,255,255,.5);--dash-placeholder-color: #0a84ff;--dash-resize-handle-color: rgba(255,255,255,.25)}.dashboard-wrapper{display:flex;flex:1;min-height:0;overflow:hidden;padding:16px;box-sizing:border-box;background:var(--dash-bg);color:var(--dash-fore-color);font-family:var(--dash-font-family)}.main-content{flex:1;display:flex;flex-direction:column;overflow:hidden;border-radius:40px;padding:20px;background:var(--dash-panel-bg);border:1px solid
|
|
1409
|
+
`, styles: [":host{display:flex;flex-direction:column;width:100%;height:100%;min-height:0;--dash-bg: #000000;--dash-panel-bg: #1c1c1e;--dash-panel-border: rgba(255,255,255,.05);--dash-card-bg: #2c2c2e;--dash-fore-color: #8e8e93;--dash-accent-color: #0a84ff;--dash-accent-tint: rgba(10,132,255,.15);--dash-accent-tint-strong: rgba(10,132,255,.25);--dash-danger-color: #ff453a;--dash-danger-tint: rgba(255,69,58,.22);--dash-font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif;--dash-tabs-container-color: rgba(44,44,46,.6);--dash-tab-active-color: #3a3a3c;--dash-tab-active-text: #ffffff;--dash-tab-hover-text: #e5e5ea;--dash-tab-hover-bg: rgba(255,255,255,.05);--dash-add-widget-text: #ffffff;--dash-overflow-btn-bg: rgba(255,255,255,.08);--dash-overflow-btn-border: rgba(255,255,255,.12);--dash-overlay-bg: rgba(0,0,0,.55);--dash-sheet-border: rgba(255,255,255,.08);--dash-scrollbar-color: rgba(255,255,255,.15);--dash-popout-title-color: #ffffff;--dash-widget-title-color: #ffffff;--dash-drag-handle-color: rgba(255,255,255,.2);--dash-widget-border-color: rgba(255,255,255,.07);--dash-widget-btn-bg: rgba(255,255,255,.07);--dash-widget-btn-color: rgba(255,255,255,.5);--dash-placeholder-color: #0a84ff;--dash-resize-handle-color: rgba(255,255,255,.25)}.dashboard-wrapper{display:flex;flex:1;min-height:0;overflow:hidden;padding:16px;box-sizing:border-box;background:var(--dash-bg);color:var(--dash-fore-color);font-family:var(--dash-font-family)}.main-content{flex:1;display:flex;flex-direction:column;overflow:hidden;border-radius:40px;padding:20px;background:var(--dash-panel-bg);border:1px solid var(--dash-panel-border);position:relative}.popout-wrapper{display:flex;flex:1;flex-direction:column;min-height:0;overflow:hidden;padding:16px;box-sizing:border-box;background:var(--dash-bg);color:var(--dash-fore-color);font-family:var(--dash-font-family)}.popout-header{display:flex;align-items:center;margin-bottom:16px;padding:10px 18px;background:var(--dash-panel-bg);border-radius:20px;border:1px solid var(--dash-panel-border);flex-shrink:0}.popout-title{font-size:16px;font-weight:700;color:var(--dash-popout-title-color)}.popout-wrapper app-grid{flex:1;min-height:0;overflow:auto}.dashboard-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:20px;gap:12px;flex-shrink:0}.tabs-track{display:flex;align-items:center;gap:6px;flex:1;min-width:0;background:var(--dash-tabs-container-color);backdrop-filter:blur(10px);border-radius:25px;padding:6px;overflow:hidden}.tab{display:flex;align-items:center;gap:4px;padding:8px 14px 8px 18px;border-radius:20px;cursor:pointer;font-size:14px;font-weight:500;color:var(--dash-fore-color);white-space:nowrap;flex-shrink:0;transition:background .2s,color .2s}.tab.active{background:var(--dash-tab-active-color);color:var(--dash-tab-active-text)}.tab:hover:not(.active){color:var(--dash-tab-hover-text);background:var(--dash-tab-hover-bg)}.tab-name{white-space:nowrap}.tab-action{background:transparent;border:none;color:var(--dash-fore-color);padding:2px;font-size:11px;cursor:pointer;opacity:0;border-radius:50%;width:20px;height:20px;display:flex;align-items:center;justify-content:center;transition:all .2s;flex-shrink:0}.tab:hover .tab-action{opacity:1}.tab-popout:hover{color:var(--dash-accent-color);background:var(--dash-accent-tint)}.tab-close:hover{color:var(--dash-danger-color);background:var(--dash-danger-tint)}.btn-overflow{display:inline-flex;align-items:center;gap:5px;background:var(--dash-overflow-btn-bg);border:1px solid var(--dash-overflow-btn-border);border-radius:20px;padding:7px 13px;cursor:pointer;font-size:13px;font-weight:600;color:var(--dash-tab-active-text);white-space:nowrap;flex-shrink:0;transition:background .2s,border-color .2s,color .2s}.btn-overflow:hover{background:var(--dash-accent-tint);border-color:var(--dash-accent-color);color:var(--dash-accent-color)}.btn-add-page{background:transparent;border:none;color:var(--dash-fore-color);padding:8px 14px;cursor:pointer;border-radius:20px;flex-shrink:0;transition:all .2s}.btn-add-page:hover{color:var(--dash-accent-color);background:var(--dash-accent-tint)}.tab-switcher-overlay{position:absolute;inset:0;z-index:1000;background:var(--dash-overlay-bg);backdrop-filter:blur(4px);display:flex;align-items:flex-start;justify-content:center;padding-top:60px;animation:overlay-in .18s ease}@keyframes overlay-in{0%{opacity:0}to{opacity:1}}.tab-switcher-sheet{background:var(--dash-panel-bg);border:1px solid var(--dash-sheet-border);border-radius:24px;width:min(520px,calc(100% - 32px));max-height:calc(100% - 80px);display:flex;flex-direction:column;overflow:hidden;animation:sheet-in .22s cubic-bezier(.32,1.2,.5,1)}@keyframes sheet-in{0%{transform:translateY(-16px) scale(.97);opacity:0}to{transform:translateY(0) scale(1);opacity:1}}.tab-switcher-header{display:flex;align-items:center;justify-content:space-between;padding:16px 20px 12px;border-bottom:1px solid var(--dash-sheet-border);flex-shrink:0}.tab-switcher-title{font-size:15px;font-weight:700;color:var(--dash-tab-active-text)}.tab-switcher-close-btn{background:var(--dash-widget-btn-bg);border:none;color:var(--dash-fore-color);width:28px;height:28px;border-radius:50%;display:flex;align-items:center;justify-content:center;cursor:pointer;font-size:14px;transition:background .15s,color .15s}.tab-switcher-close-btn:hover{background:var(--dash-danger-tint);color:var(--dash-danger-color)}.tab-switcher-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(140px,1fr));gap:10px;padding:16px;overflow-y:auto;scrollbar-width:thin;scrollbar-color:var(--dash-scrollbar-color) transparent}.tab-card{display:flex;flex-direction:column;align-items:center;gap:8px;padding:16px 12px 12px;border-radius:16px;background:var(--dash-card-bg);border:1.5px solid transparent;cursor:pointer;transition:border-color .15s,background .15s,transform .12s;position:relative}.tab-card:hover{background:var(--dash-tab-hover-bg);transform:translateY(-2px)}.tab-card.active{border-color:var(--dash-accent-color);background:var(--dash-accent-tint)}.tab-card-icon{width:44px;height:44px;border-radius:12px;background:var(--dash-widget-btn-bg);display:flex;align-items:center;justify-content:center;font-size:20px;color:var(--dash-fore-color)}.tab-card.active .tab-card-icon{background:var(--dash-accent-tint-strong);color:var(--dash-accent-color)}.tab-card-name{font-size:13px;font-weight:500;color:var(--dash-tab-active-text);text-align:center;word-break:break-word;line-height:1.3}.tab-card-actions{display:flex;gap:4px;opacity:0;transition:opacity .15s}.tab-card:hover .tab-card-actions{opacity:1}.tab-card-btn{background:var(--dash-widget-btn-bg);border:none;width:26px;height:26px;border-radius:50%;display:flex;align-items:center;justify-content:center;cursor:pointer;font-size:12px;color:var(--dash-fore-color);transition:background .15s,color .15s}.tab-card-popout:hover{background:var(--dash-accent-tint-strong);color:var(--dash-accent-color)}.tab-card-remove:hover{background:var(--dash-danger-tint);color:var(--dash-danger-color)}.tab-switcher-footer{padding:12px 16px 16px;border-top:1px solid var(--dash-sheet-border);flex-shrink:0}.tab-switcher-add-btn{display:flex;align-items:center;justify-content:center;gap:8px;width:100%;padding:11px 20px;border-radius:16px;background:var(--dash-accent-tint);border:1.5px dashed var(--dash-accent-tint-strong);color:var(--dash-accent-color);font-size:14px;font-weight:600;cursor:pointer;transition:background .15s,border-color .15s}.tab-switcher-add-btn:hover{background:var(--dash-accent-tint-strong);border-color:var(--dash-accent-color)}.btn-add-widget{display:flex;align-items:center;gap:6px;background:var(--dash-accent-color);border:none;color:var(--dash-add-widget-text);font-size:14px;font-weight:600;padding:10px 20px;border-radius:22px;cursor:pointer;white-space:nowrap;flex-shrink:0;transition:opacity .2s,transform .15s}.btn-add-widget:hover{opacity:.9;transform:translateY(-1px)}.btn-add-widget:active{transform:translateY(0)}.main-content .grid-container{flex:1;overflow:auto;border-radius:24px;min-height:0}app-grid{background:transparent;display:block;height:100%;width:100%}\n"] }]
|
|
1211
1410
|
}], ctorParameters: function () { return [{ type: DashboardStateService }, { type: i0.ChangeDetectorRef }, { type: i0.NgZone }]; }, propDecorators: { initialLayout: [{
|
|
1212
1411
|
type: Input
|
|
1213
1412
|
}], theme: [{
|
|
@@ -1219,9 +1418,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImpo
|
|
|
1219
1418
|
}], cellTemplate: [{
|
|
1220
1419
|
type: ContentChild,
|
|
1221
1420
|
args: [GridCellDirective, { read: TemplateRef }]
|
|
1222
|
-
}],
|
|
1421
|
+
}], tabsTrackRef: [{
|
|
1223
1422
|
type: ViewChild,
|
|
1224
|
-
args: ['
|
|
1423
|
+
args: ['tabsTrack']
|
|
1424
|
+
}], onEscapeKey: [{
|
|
1425
|
+
type: HostListener,
|
|
1426
|
+
args: ['document:keydown.escape']
|
|
1225
1427
|
}] } });
|
|
1226
1428
|
|
|
1227
1429
|
class DashboardModule {
|