@ogidor/dashboard 1.0.9 → 1.0.11
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 +22 -0
- package/app/app.module.d.ts +1 -1
- package/app/dashboard.component.d.ts +15 -15
- package/esm2020/app/app.module.mjs +6 -2
- package/esm2020/app/dashboard.component.mjs +140 -121
- package/fesm2015/ogidor-dashboard.mjs +146 -121
- package/fesm2015/ogidor-dashboard.mjs.map +1 -1
- package/fesm2020/ogidor-dashboard.mjs +144 -121
- package/fesm2020/ogidor-dashboard.mjs.map +1 -1
- package/package.json +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { Directive, EventEmitter, TemplateRef, Component, ChangeDetectionStrategy, Input, Output, ViewChild, ContentChild, Injectable, NgModule } from '@angular/core';
|
|
2
|
+
import { Directive, EventEmitter, TemplateRef, Component, ChangeDetectionStrategy, Input, Output, ViewChild, ContentChild, Injectable, Pipe, 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';
|
|
@@ -885,42 +885,44 @@ const DEFAULT_THEME = {
|
|
|
885
885
|
accentColor: '#0a84ff',
|
|
886
886
|
dangerColor: '#ff453a',
|
|
887
887
|
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif',
|
|
888
|
-
// Header / Tabs
|
|
889
888
|
tabsContainerColor: 'rgba(44,44,46,0.6)',
|
|
890
889
|
tabActiveColor: '#3a3a3c',
|
|
891
890
|
tabActiveTextColor: '#ffffff',
|
|
892
891
|
tabHoverTextColor: '#e5e5ea',
|
|
893
892
|
addWidgetButtonTextColor: '#ffffff',
|
|
894
|
-
// Pop-out
|
|
895
893
|
popoutTitleColor: '#ffffff',
|
|
896
|
-
// Widget card
|
|
897
894
|
widgetTitleColor: '#ffffff',
|
|
898
895
|
dragHandleColor: 'rgba(255,255,255,0.2)',
|
|
899
896
|
widgetBorderColor: 'rgba(255,255,255,0.07)',
|
|
900
897
|
widgetButtonBgColor: 'rgba(255,255,255,0.07)',
|
|
901
898
|
widgetButtonColor: 'rgba(255,255,255,0.5)',
|
|
902
|
-
// Grid
|
|
903
899
|
placeholderColor: '#0a84ff',
|
|
904
900
|
resizeHandleColor: 'rgba(255,255,255,0.25)',
|
|
905
901
|
};
|
|
902
|
+
class OverflowActivePipe {
|
|
903
|
+
transform(pages, activePageId) {
|
|
904
|
+
return pages.some(p => p.id === activePageId);
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
OverflowActivePipe.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: OverflowActivePipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
|
|
908
|
+
OverflowActivePipe.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "15.2.10", ngImport: i0, type: OverflowActivePipe, name: "overflowActive" });
|
|
909
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: OverflowActivePipe, decorators: [{
|
|
910
|
+
type: Pipe,
|
|
911
|
+
args: [{ name: 'overflowActive' }]
|
|
912
|
+
}] });
|
|
906
913
|
class DashboardComponent {
|
|
907
|
-
constructor(stateService) {
|
|
914
|
+
constructor(stateService, cdr, zone) {
|
|
908
915
|
this.stateService = stateService;
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
* The consumer should open their own dialog and call `stateService.addWidget(...)`.
|
|
912
|
-
*/
|
|
916
|
+
this.cdr = cdr;
|
|
917
|
+
this.zone = zone;
|
|
913
918
|
this.addWidgetRequested = new EventEmitter();
|
|
914
|
-
/**
|
|
915
|
-
* Emits the Widget when the user clicks the edit (pen) icon on a card.
|
|
916
|
-
* The consumer should open their own edit UI and call `stateService.updateWidget(...)`.
|
|
917
|
-
*/
|
|
918
919
|
this.editWidgetRequested = new EventEmitter();
|
|
919
920
|
this.resolvedTheme = { ...DEFAULT_THEME };
|
|
920
921
|
this.wrapperStyles = {};
|
|
921
922
|
this.pages = [];
|
|
922
923
|
this.activePageId = '';
|
|
923
924
|
this.isPoppedOut = false;
|
|
925
|
+
this.tabsOverflow = false;
|
|
924
926
|
this.subs = new Subscription();
|
|
925
927
|
}
|
|
926
928
|
ngOnChanges(changes) {
|
|
@@ -943,8 +945,7 @@ class DashboardComponent {
|
|
|
943
945
|
this.subs.add(this.stateService.pages$.subscribe(pages => {
|
|
944
946
|
this.pages = pages;
|
|
945
947
|
if (this.isPoppedOut && hash) {
|
|
946
|
-
|
|
947
|
-
if (target)
|
|
948
|
+
if (pages.find(p => p.id === hash))
|
|
948
949
|
this.stateService.setActivePage(hash);
|
|
949
950
|
}
|
|
950
951
|
this.updateActivePage();
|
|
@@ -954,7 +955,24 @@ class DashboardComponent {
|
|
|
954
955
|
this.updateActivePage();
|
|
955
956
|
}));
|
|
956
957
|
}
|
|
957
|
-
|
|
958
|
+
ngAfterViewInit() {
|
|
959
|
+
if (!this.tabsContainerRef)
|
|
960
|
+
return;
|
|
961
|
+
this.zone.runOutsideAngular(() => {
|
|
962
|
+
this.resizeObserver = new ResizeObserver(() => {
|
|
963
|
+
this.zone.run(() => this.checkOverflow());
|
|
964
|
+
});
|
|
965
|
+
this.resizeObserver.observe(this.tabsContainerRef.nativeElement);
|
|
966
|
+
});
|
|
967
|
+
// Also detect scroll so fade hides when scrolled to end
|
|
968
|
+
this.tabsContainerRef.nativeElement.addEventListener('scroll', () => {
|
|
969
|
+
this.zone.run(() => this.checkOverflow());
|
|
970
|
+
});
|
|
971
|
+
}
|
|
972
|
+
ngOnDestroy() {
|
|
973
|
+
this.subs.unsubscribe();
|
|
974
|
+
this.resizeObserver?.disconnect();
|
|
975
|
+
}
|
|
958
976
|
onItemChanged(widget) {
|
|
959
977
|
this.stateService.updateWidgetPosition(this.activePageId, widget.id, widget.x, widget.y, widget.cols, widget.rows);
|
|
960
978
|
}
|
|
@@ -975,6 +993,14 @@ class DashboardComponent {
|
|
|
975
993
|
this.stateService.popOutPage(pageId);
|
|
976
994
|
}
|
|
977
995
|
serializeLayout() { return this.stateService.serializeLayout(); }
|
|
996
|
+
checkOverflow() {
|
|
997
|
+
const el = this.tabsContainerRef?.nativeElement;
|
|
998
|
+
if (!el)
|
|
999
|
+
return;
|
|
1000
|
+
const atEnd = el.scrollLeft + el.clientWidth >= el.scrollWidth - 4;
|
|
1001
|
+
this.tabsOverflow = !atEnd && el.scrollWidth > el.clientWidth;
|
|
1002
|
+
this.cdr.markForCheck();
|
|
1003
|
+
}
|
|
978
1004
|
applyTheme() {
|
|
979
1005
|
this.resolvedTheme = { ...DEFAULT_THEME, ...(this.theme ?? {}) };
|
|
980
1006
|
const t = this.resolvedTheme;
|
|
@@ -1003,86 +1029,85 @@ class DashboardComponent {
|
|
|
1003
1029
|
}
|
|
1004
1030
|
updateActivePage() {
|
|
1005
1031
|
this.activePage = this.pages.find(p => p.id === this.activePageId);
|
|
1032
|
+
// scroll active tab into view
|
|
1033
|
+
setTimeout(() => {
|
|
1034
|
+
if (!this.tabsContainerRef)
|
|
1035
|
+
return;
|
|
1036
|
+
const active = this.tabsContainerRef.nativeElement.querySelector('.tab.active');
|
|
1037
|
+
active?.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'nearest' });
|
|
1038
|
+
this.checkOverflow();
|
|
1039
|
+
});
|
|
1006
1040
|
}
|
|
1007
1041
|
}
|
|
1008
|
-
DashboardComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: DashboardComponent, deps: [{ token: DashboardStateService }], target: i0.ɵɵFactoryTarget.Component });
|
|
1009
|
-
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 }], usesOnChanges: true, ngImport: i0, template: `
|
|
1010
|
-
<!--
|
|
1011
|
-
POPPED-OUT MODE
|
|
1012
|
-
══════════════════════════════════════════════════════════ -->
|
|
1042
|
+
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 });
|
|
1043
|
+
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: "tabsContainerRef", first: true, predicate: ["tabsContainer"], descendants: true }], usesOnChanges: true, ngImport: i0, template: `
|
|
1044
|
+
<!-- POPPED-OUT MODE -->
|
|
1013
1045
|
<ng-container *ngIf="isPoppedOut; else normalMode">
|
|
1014
1046
|
<div class="popout-wrapper" [ngStyle]="wrapperStyles">
|
|
1015
1047
|
<header class="popout-header">
|
|
1016
1048
|
<span class="popout-title">{{ activePage?.name }}</span>
|
|
1017
1049
|
</header>
|
|
1018
|
-
<
|
|
1019
|
-
<
|
|
1020
|
-
<
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
>
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
[ngTemplateOutletContext]="{ widget: widget }">
|
|
1031
|
-
</ng-container>
|
|
1032
|
-
</app-widget-renderer>
|
|
1033
|
-
</ng-template>
|
|
1034
|
-
</app-grid>
|
|
1035
|
-
</div>
|
|
1050
|
+
<app-grid [widgets]="activePage?.widgets || []" (itemChanged)="onItemChanged($event)">
|
|
1051
|
+
<ng-template gridCell let-widget="widget">
|
|
1052
|
+
<app-widget-renderer [widget]="widget" [theme]="resolvedTheme"
|
|
1053
|
+
(editRequested)="editWidgetRequested.emit($event)"
|
|
1054
|
+
(removeRequested)="onRemoveWidget($event)">
|
|
1055
|
+
<ng-container *ngIf="cellTemplate"
|
|
1056
|
+
[ngTemplateOutlet]="cellTemplate"
|
|
1057
|
+
[ngTemplateOutletContext]="{ widget: widget }">
|
|
1058
|
+
</ng-container>
|
|
1059
|
+
</app-widget-renderer>
|
|
1060
|
+
</ng-template>
|
|
1061
|
+
</app-grid>
|
|
1036
1062
|
</div>
|
|
1037
1063
|
</ng-container>
|
|
1038
1064
|
|
|
1039
|
-
<!--
|
|
1040
|
-
NORMAL MODE
|
|
1041
|
-
══════════════════════════════════════════════════════════ -->
|
|
1065
|
+
<!-- NORMAL MODE -->
|
|
1042
1066
|
<ng-template #normalMode>
|
|
1043
1067
|
<div class="dashboard-wrapper" [ngStyle]="wrapperStyles">
|
|
1044
1068
|
<main class="main-content">
|
|
1045
1069
|
|
|
1046
1070
|
<header class="dashboard-header">
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1071
|
+
|
|
1072
|
+
<!-- Tabs pill — scrollable, fades when overflowing -->
|
|
1073
|
+
<div class="tabs-scroll-track">
|
|
1074
|
+
<div class="tabs-container" #tabsContainer>
|
|
1075
|
+
<div
|
|
1076
|
+
*ngFor="let page of pages"
|
|
1077
|
+
class="tab"
|
|
1078
|
+
[class.active]="page.id === activePageId"
|
|
1079
|
+
(click)="onSelectPage(page.id)"
|
|
1080
|
+
>
|
|
1081
|
+
<span class="tab-name">{{ page.name }}</span>
|
|
1082
|
+
<button class="tab-action tab-popout" (click)="onPopOut($event, page.id)" title="Open in new window">
|
|
1083
|
+
<i class="la la-external-link-alt"></i>
|
|
1084
|
+
</button>
|
|
1085
|
+
<button class="tab-action tab-close" *ngIf="pages.length > 1" (click)="onRemovePage($event, page.id)" title="Close">
|
|
1086
|
+
<i class="la la-times"></i>
|
|
1087
|
+
</button>
|
|
1088
|
+
</div>
|
|
1089
|
+
<button class="btn-add-page" (click)="onAddPage()" title="New workspace">
|
|
1090
|
+
<i class="la la-plus"></i>
|
|
1060
1091
|
</button>
|
|
1061
1092
|
</div>
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
</button>
|
|
1093
|
+
<!-- fade hint shown when scrollable -->
|
|
1094
|
+
<div class="tabs-fade-right" *ngIf="tabsOverflow"></div>
|
|
1065
1095
|
</div>
|
|
1066
1096
|
|
|
1067
|
-
<!-- Add Widget button — consumer decides what happens -->
|
|
1068
1097
|
<button class="btn-add-widget" (click)="addWidgetRequested.emit()" title="Add widget">
|
|
1069
1098
|
<i class="la la-plus"></i>
|
|
1070
1099
|
<span>Add Widget</span>
|
|
1071
1100
|
</button>
|
|
1101
|
+
|
|
1072
1102
|
</header>
|
|
1073
1103
|
|
|
1074
1104
|
<div class="grid-container">
|
|
1075
1105
|
<app-grid [widgets]="activePage?.widgets || []" (itemChanged)="onItemChanged($event)">
|
|
1076
1106
|
<ng-template gridCell let-widget="widget">
|
|
1077
|
-
<app-widget-renderer
|
|
1078
|
-
[widget]="widget"
|
|
1079
|
-
[theme]="resolvedTheme"
|
|
1107
|
+
<app-widget-renderer [widget]="widget" [theme]="resolvedTheme"
|
|
1080
1108
|
(editRequested)="editWidgetRequested.emit($event)"
|
|
1081
|
-
(removeRequested)="onRemoveWidget($event)"
|
|
1082
|
-
|
|
1083
|
-
<!-- Stamp consumer's cell template (if provided) inside the card body -->
|
|
1084
|
-
<ng-container
|
|
1085
|
-
*ngIf="cellTemplate"
|
|
1109
|
+
(removeRequested)="onRemoveWidget($event)">
|
|
1110
|
+
<ng-container *ngIf="cellTemplate"
|
|
1086
1111
|
[ngTemplateOutlet]="cellTemplate"
|
|
1087
1112
|
[ngTemplateOutletContext]="{ widget: widget }">
|
|
1088
1113
|
</ng-container>
|
|
@@ -1094,86 +1119,77 @@ DashboardComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", ver
|
|
|
1094
1119
|
</main>
|
|
1095
1120
|
</div>
|
|
1096
1121
|
</ng-template>
|
|
1097
|
-
`, isInline: true, styles: [":host{display:
|
|
1122
|
+
`, 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 rgba(255,255,255,.05)}.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 rgba(255,255,255,.06);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-scroll-track{position:relative;flex:1;min-width:0}.tabs-container{display:flex;align-items:center;gap:8px;background:var(--dash-tabs-container-color);backdrop-filter:blur(10px);border-radius:25px;padding:6px;overflow-x:auto;overflow-y:hidden;scrollbar-width:none}.tabs-container::-webkit-scrollbar{display:none}.tabs-fade-right{position:absolute;top:0;right:0;width:60px;height:100%;border-radius:0 25px 25px 0;pointer-events:none;background:linear-gradient(to right,transparent,var(--dash-panel-bg))}.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:all .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:rgba(255,255,255,.05)}.tab-name{flex:1;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:rgba(10,132,255,.18)}.tab-close:hover{color:var(--dash-danger-color);background:rgba(255,69,58,.2)}.btn-add-page{background:transparent;border:none;color:var(--dash-fore-color);padding:8px 16px;cursor:pointer;border-radius:20px;flex-shrink:0;transition:all .2s}.btn-add-page:hover{color:var(--dash-accent-color);background:rgba(10,132,255,.1)}.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]" }] });
|
|
1098
1123
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: DashboardComponent, decorators: [{
|
|
1099
1124
|
type: Component,
|
|
1100
1125
|
args: [{ selector: 'app-dashboard', template: `
|
|
1101
|
-
<!--
|
|
1102
|
-
POPPED-OUT MODE
|
|
1103
|
-
══════════════════════════════════════════════════════════ -->
|
|
1126
|
+
<!-- POPPED-OUT MODE -->
|
|
1104
1127
|
<ng-container *ngIf="isPoppedOut; else normalMode">
|
|
1105
1128
|
<div class="popout-wrapper" [ngStyle]="wrapperStyles">
|
|
1106
1129
|
<header class="popout-header">
|
|
1107
1130
|
<span class="popout-title">{{ activePage?.name }}</span>
|
|
1108
1131
|
</header>
|
|
1109
|
-
<
|
|
1110
|
-
<
|
|
1111
|
-
<
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
>
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
[ngTemplateOutletContext]="{ widget: widget }">
|
|
1122
|
-
</ng-container>
|
|
1123
|
-
</app-widget-renderer>
|
|
1124
|
-
</ng-template>
|
|
1125
|
-
</app-grid>
|
|
1126
|
-
</div>
|
|
1132
|
+
<app-grid [widgets]="activePage?.widgets || []" (itemChanged)="onItemChanged($event)">
|
|
1133
|
+
<ng-template gridCell let-widget="widget">
|
|
1134
|
+
<app-widget-renderer [widget]="widget" [theme]="resolvedTheme"
|
|
1135
|
+
(editRequested)="editWidgetRequested.emit($event)"
|
|
1136
|
+
(removeRequested)="onRemoveWidget($event)">
|
|
1137
|
+
<ng-container *ngIf="cellTemplate"
|
|
1138
|
+
[ngTemplateOutlet]="cellTemplate"
|
|
1139
|
+
[ngTemplateOutletContext]="{ widget: widget }">
|
|
1140
|
+
</ng-container>
|
|
1141
|
+
</app-widget-renderer>
|
|
1142
|
+
</ng-template>
|
|
1143
|
+
</app-grid>
|
|
1127
1144
|
</div>
|
|
1128
1145
|
</ng-container>
|
|
1129
1146
|
|
|
1130
|
-
<!--
|
|
1131
|
-
NORMAL MODE
|
|
1132
|
-
══════════════════════════════════════════════════════════ -->
|
|
1147
|
+
<!-- NORMAL MODE -->
|
|
1133
1148
|
<ng-template #normalMode>
|
|
1134
1149
|
<div class="dashboard-wrapper" [ngStyle]="wrapperStyles">
|
|
1135
1150
|
<main class="main-content">
|
|
1136
1151
|
|
|
1137
1152
|
<header class="dashboard-header">
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1153
|
+
|
|
1154
|
+
<!-- Tabs pill — scrollable, fades when overflowing -->
|
|
1155
|
+
<div class="tabs-scroll-track">
|
|
1156
|
+
<div class="tabs-container" #tabsContainer>
|
|
1157
|
+
<div
|
|
1158
|
+
*ngFor="let page of pages"
|
|
1159
|
+
class="tab"
|
|
1160
|
+
[class.active]="page.id === activePageId"
|
|
1161
|
+
(click)="onSelectPage(page.id)"
|
|
1162
|
+
>
|
|
1163
|
+
<span class="tab-name">{{ page.name }}</span>
|
|
1164
|
+
<button class="tab-action tab-popout" (click)="onPopOut($event, page.id)" title="Open in new window">
|
|
1165
|
+
<i class="la la-external-link-alt"></i>
|
|
1166
|
+
</button>
|
|
1167
|
+
<button class="tab-action tab-close" *ngIf="pages.length > 1" (click)="onRemovePage($event, page.id)" title="Close">
|
|
1168
|
+
<i class="la la-times"></i>
|
|
1169
|
+
</button>
|
|
1170
|
+
</div>
|
|
1171
|
+
<button class="btn-add-page" (click)="onAddPage()" title="New workspace">
|
|
1172
|
+
<i class="la la-plus"></i>
|
|
1151
1173
|
</button>
|
|
1152
1174
|
</div>
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
</button>
|
|
1175
|
+
<!-- fade hint shown when scrollable -->
|
|
1176
|
+
<div class="tabs-fade-right" *ngIf="tabsOverflow"></div>
|
|
1156
1177
|
</div>
|
|
1157
1178
|
|
|
1158
|
-
<!-- Add Widget button — consumer decides what happens -->
|
|
1159
1179
|
<button class="btn-add-widget" (click)="addWidgetRequested.emit()" title="Add widget">
|
|
1160
1180
|
<i class="la la-plus"></i>
|
|
1161
1181
|
<span>Add Widget</span>
|
|
1162
1182
|
</button>
|
|
1183
|
+
|
|
1163
1184
|
</header>
|
|
1164
1185
|
|
|
1165
1186
|
<div class="grid-container">
|
|
1166
1187
|
<app-grid [widgets]="activePage?.widgets || []" (itemChanged)="onItemChanged($event)">
|
|
1167
1188
|
<ng-template gridCell let-widget="widget">
|
|
1168
|
-
<app-widget-renderer
|
|
1169
|
-
[widget]="widget"
|
|
1170
|
-
[theme]="resolvedTheme"
|
|
1189
|
+
<app-widget-renderer [widget]="widget" [theme]="resolvedTheme"
|
|
1171
1190
|
(editRequested)="editWidgetRequested.emit($event)"
|
|
1172
|
-
(removeRequested)="onRemoveWidget($event)"
|
|
1173
|
-
|
|
1174
|
-
<!-- Stamp consumer's cell template (if provided) inside the card body -->
|
|
1175
|
-
<ng-container
|
|
1176
|
-
*ngIf="cellTemplate"
|
|
1191
|
+
(removeRequested)="onRemoveWidget($event)">
|
|
1192
|
+
<ng-container *ngIf="cellTemplate"
|
|
1177
1193
|
[ngTemplateOutlet]="cellTemplate"
|
|
1178
1194
|
[ngTemplateOutletContext]="{ widget: widget }">
|
|
1179
1195
|
</ng-container>
|
|
@@ -1185,8 +1201,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImpo
|
|
|
1185
1201
|
</main>
|
|
1186
1202
|
</div>
|
|
1187
1203
|
</ng-template>
|
|
1188
|
-
`, styles: [":host{display:
|
|
1189
|
-
}], ctorParameters: function () { return [{ type: DashboardStateService }]; }, propDecorators: { initialLayout: [{
|
|
1204
|
+
`, 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 rgba(255,255,255,.05)}.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 rgba(255,255,255,.06);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-scroll-track{position:relative;flex:1;min-width:0}.tabs-container{display:flex;align-items:center;gap:8px;background:var(--dash-tabs-container-color);backdrop-filter:blur(10px);border-radius:25px;padding:6px;overflow-x:auto;overflow-y:hidden;scrollbar-width:none}.tabs-container::-webkit-scrollbar{display:none}.tabs-fade-right{position:absolute;top:0;right:0;width:60px;height:100%;border-radius:0 25px 25px 0;pointer-events:none;background:linear-gradient(to right,transparent,var(--dash-panel-bg))}.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:all .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:rgba(255,255,255,.05)}.tab-name{flex:1;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:rgba(10,132,255,.18)}.tab-close:hover{color:var(--dash-danger-color);background:rgba(255,69,58,.2)}.btn-add-page{background:transparent;border:none;color:var(--dash-fore-color);padding:8px 16px;cursor:pointer;border-radius:20px;flex-shrink:0;transition:all .2s}.btn-add-page:hover{color:var(--dash-accent-color);background:rgba(10,132,255,.1)}.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"] }]
|
|
1205
|
+
}], ctorParameters: function () { return [{ type: DashboardStateService }, { type: i0.ChangeDetectorRef }, { type: i0.NgZone }]; }, propDecorators: { initialLayout: [{
|
|
1190
1206
|
type: Input
|
|
1191
1207
|
}], theme: [{
|
|
1192
1208
|
type: Input
|
|
@@ -1197,15 +1213,20 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImpo
|
|
|
1197
1213
|
}], cellTemplate: [{
|
|
1198
1214
|
type: ContentChild,
|
|
1199
1215
|
args: [GridCellDirective, { read: TemplateRef }]
|
|
1216
|
+
}], tabsContainerRef: [{
|
|
1217
|
+
type: ViewChild,
|
|
1218
|
+
args: ['tabsContainer']
|
|
1200
1219
|
}] } });
|
|
1201
1220
|
|
|
1202
1221
|
class DashboardModule {
|
|
1203
1222
|
}
|
|
1204
1223
|
DashboardModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: DashboardModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
|
|
1205
1224
|
DashboardModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "15.2.10", ngImport: i0, type: DashboardModule, declarations: [DashboardComponent,
|
|
1225
|
+
OverflowActivePipe,
|
|
1206
1226
|
WidgetRendererComponent,
|
|
1207
1227
|
CustomGridComponent,
|
|
1208
1228
|
GridCellDirective], imports: [CommonModule], exports: [DashboardComponent,
|
|
1229
|
+
OverflowActivePipe,
|
|
1209
1230
|
WidgetRendererComponent,
|
|
1210
1231
|
CustomGridComponent,
|
|
1211
1232
|
GridCellDirective] });
|
|
@@ -1215,6 +1236,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImpo
|
|
|
1215
1236
|
args: [{
|
|
1216
1237
|
declarations: [
|
|
1217
1238
|
DashboardComponent,
|
|
1239
|
+
OverflowActivePipe,
|
|
1218
1240
|
WidgetRendererComponent,
|
|
1219
1241
|
CustomGridComponent,
|
|
1220
1242
|
GridCellDirective,
|
|
@@ -1225,6 +1247,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImpo
|
|
|
1225
1247
|
providers: [DashboardStateService],
|
|
1226
1248
|
exports: [
|
|
1227
1249
|
DashboardComponent,
|
|
1250
|
+
OverflowActivePipe,
|
|
1228
1251
|
WidgetRendererComponent,
|
|
1229
1252
|
CustomGridComponent,
|
|
1230
1253
|
GridCellDirective,
|
|
@@ -1256,5 +1279,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImpo
|
|
|
1256
1279
|
* Generated bundle index. Do not edit.
|
|
1257
1280
|
*/
|
|
1258
1281
|
|
|
1259
|
-
export { AppModule, CustomGridComponent, DashboardComponent, DashboardModule, DashboardStateService, GridCellDirective, WidgetRendererComponent };
|
|
1282
|
+
export { AppModule, CustomGridComponent, DashboardComponent, DashboardModule, DashboardStateService, GridCellDirective, OverflowActivePipe, WidgetRendererComponent };
|
|
1260
1283
|
//# sourceMappingURL=ogidor-dashboard.mjs.map
|