@quicdata/analytics 0.0.7 → 0.0.9
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/filters/widget-toolbar.js +1 -1
- package/package.json +1 -1
- package/widgets/analytics-report.d.ts +2 -0
- package/widgets/analytics-report.d.ts.map +1 -1
- package/widgets/analytics-report.js +48 -2
- package/widgets/base-chart.d.ts +1 -0
- package/widgets/base-chart.d.ts.map +1 -1
- package/widgets/base-chart.js +252 -16
- package/widgets/table.d.ts +3 -0
- package/widgets/table.d.ts.map +1 -1
- package/widgets/table.js +179 -21
|
@@ -122,7 +122,7 @@ let AnalyticsWidgetToolbar = class AnalyticsWidgetToolbar extends LitElement {
|
|
|
122
122
|
static { this.styles = css `
|
|
123
123
|
:host {
|
|
124
124
|
position: absolute;
|
|
125
|
-
top: 0.5rem;
|
|
125
|
+
top: var(--analytics-widget-toolbar-top, 0.5rem);
|
|
126
126
|
right: 0.5rem;
|
|
127
127
|
z-index: var(--analytics-widget-toolbar-z-index, 20);
|
|
128
128
|
opacity: 0;
|
package/package.json
CHANGED
|
@@ -26,6 +26,7 @@ export declare class AnalyticsReport extends LitElement {
|
|
|
26
26
|
private _excelUrlBase;
|
|
27
27
|
private _loading;
|
|
28
28
|
private _error;
|
|
29
|
+
private _refreshKey;
|
|
29
30
|
constructor();
|
|
30
31
|
connectedCallback(): void;
|
|
31
32
|
disconnectedCallback(): void;
|
|
@@ -39,6 +40,7 @@ export declare class AnalyticsReport extends LitElement {
|
|
|
39
40
|
private get _pdfUrl();
|
|
40
41
|
private get _excelUrl();
|
|
41
42
|
private _print;
|
|
43
|
+
private _onRefresh;
|
|
42
44
|
render(): import("lit-html").TemplateResult<1> | typeof nothing;
|
|
43
45
|
}
|
|
44
46
|
declare global {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"analytics-report.d.ts","sourceRoot":"","sources":["../../../../libs/analytics/src/widgets/analytics-report.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAa,OAAO,EAAE,MAAM,KAAK,CAAC;AAYrD,OAAO,0BAA0B,CAAC;AAClC,OAAO,YAAY,CAAC;AAEpB;;;;GAIG;AACH,qBACa,eAAgB,SAAQ,UAAU;IAC7C,OAAgB,MAAM,
|
|
1
|
+
{"version":3,"file":"analytics-report.d.ts","sourceRoot":"","sources":["../../../../libs/analytics/src/widgets/analytics-report.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAa,OAAO,EAAE,MAAM,KAAK,CAAC;AAYrD,OAAO,0BAA0B,CAAC;AAClC,OAAO,YAAY,CAAC;AAEpB;;;;GAIG;AACH,qBACa,eAAgB,SAAQ,UAAU;IAC7C,OAAgB,MAAM,0BAiHpB;IAEF,oHAAoH;IACxD,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAC;IAEtF,0CAA0C;IACgB,MAAM,EAAE,MAAM,CAAC;IAEzE,gJAAgJ;IACjF,WAAW,EAAE,MAAM,CAAC;IAEnF,6DAA6D;IACzB,KAAK,EAAE,MAAM,CAAC;IAElD,sEAAsE;IAClC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,CAAC;IAE1F,QAAyB,KAAK,CAAS;IACvC,QAAyB,QAAQ,CAAqB;IACtD,QAAyB,OAAO,CAA4C;IAC5E,QAAyB,eAAe,CAAS;IACjD,QAAyB,WAAW,CAAS;IAC7C,QAAyB,aAAa,CAAS;IAC/C,QAAyB,QAAQ,CAAU;IAC3C,QAAyB,MAAM,CAAgB;IACtC,OAAO,CAAC,WAAW,CAAK;;IAmBxB,iBAAiB,IAAI,IAAI;IAKzB,oBAAoB,IAAI,IAAI;IAK5B,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;YAevC,WAAW;IAwBzB,wJAAwJ;IACxJ,OAAO,CAAC,+BAA+B;IAWvC,OAAO,CAAC,eAAe;IAMvB,OAAO,KAAK,QAAQ,GAGnB;IAED,OAAO,KAAK,WAAW,GAItB;IAED,OAAO,KAAK,OAAO,GAIlB;IAED,OAAO,KAAK,SAAS,GAIpB;IAED,OAAO,CAAC,MAAM;IAId,OAAO,CAAC,UAAU;IAIT,MAAM;CAgEhB;AAED,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,qBAAqB;QAC7B,kBAAkB,EAAE,eAAe,CAAC;KACrC;CACF"}
|
|
@@ -34,11 +34,12 @@ let AnalyticsReport = class AnalyticsReport extends LitElement {
|
|
|
34
34
|
padding: 0.5rem 1rem;
|
|
35
35
|
border-bottom: 1px solid var(--analytics-report-border, #e2e8f0);
|
|
36
36
|
background: var(--analytics-report-header-bg, #f8fafc);
|
|
37
|
+
color: var(--analytics-report-header-color, #1e293b);
|
|
37
38
|
}
|
|
38
39
|
.report-title {
|
|
39
40
|
font-size: 1rem;
|
|
40
41
|
font-weight: 600;
|
|
41
|
-
color: var(--analytics-report-
|
|
42
|
+
color: var(--analytics-report-header-color, #1e293b);
|
|
42
43
|
margin: 0;
|
|
43
44
|
}
|
|
44
45
|
.report-actions {
|
|
@@ -62,6 +63,29 @@ let AnalyticsReport = class AnalyticsReport extends LitElement {
|
|
|
62
63
|
background: var(--analytics-report-btn-hover-bg, #f1f5f9);
|
|
63
64
|
color: var(--analytics-report-btn-hover-color, #0f172a);
|
|
64
65
|
}
|
|
66
|
+
.btn-refresh,
|
|
67
|
+
.report-actions button.btn-refresh {
|
|
68
|
+
display: inline-flex;
|
|
69
|
+
align-items: center;
|
|
70
|
+
justify-content: center;
|
|
71
|
+
width: 1.75rem;
|
|
72
|
+
height: 1.75rem;
|
|
73
|
+
padding: 0;
|
|
74
|
+
border: none;
|
|
75
|
+
border-radius: 4px;
|
|
76
|
+
background: transparent;
|
|
77
|
+
color: var(--analytics-report-header-color, #334155);
|
|
78
|
+
cursor: pointer;
|
|
79
|
+
transition: color 0.15s ease, background-color 0.15s ease;
|
|
80
|
+
}
|
|
81
|
+
.btn-refresh:hover {
|
|
82
|
+
color: var(--analytics-report-header-color, #0f172a);
|
|
83
|
+
background: var(--analytics-report-btn-hover-bg, #f1f5f9);
|
|
84
|
+
}
|
|
85
|
+
.btn-refresh svg {
|
|
86
|
+
width: 1rem;
|
|
87
|
+
height: 1rem;
|
|
88
|
+
}
|
|
65
89
|
.report-filters {
|
|
66
90
|
flex-shrink: 0;
|
|
67
91
|
border-bottom: 1px solid var(--analytics-report-border, #e2e8f0);
|
|
@@ -103,6 +127,7 @@ let AnalyticsReport = class AnalyticsReport extends LitElement {
|
|
|
103
127
|
`; }
|
|
104
128
|
constructor() {
|
|
105
129
|
super();
|
|
130
|
+
this._refreshKey = 0;
|
|
106
131
|
this.reportId = '';
|
|
107
132
|
this.apiUrl = '';
|
|
108
133
|
this.customTitle = '';
|
|
@@ -213,6 +238,9 @@ let AnalyticsReport = class AnalyticsReport extends LitElement {
|
|
|
213
238
|
_print() {
|
|
214
239
|
window.open(this._previewUrl, '_blank', 'noopener')?.print();
|
|
215
240
|
}
|
|
241
|
+
_onRefresh() {
|
|
242
|
+
this._refreshKey = Date.now();
|
|
243
|
+
}
|
|
216
244
|
render() {
|
|
217
245
|
if (this._loading) {
|
|
218
246
|
return html `
|
|
@@ -234,6 +262,20 @@ let AnalyticsReport = class AnalyticsReport extends LitElement {
|
|
|
234
262
|
<div class="report-header">
|
|
235
263
|
<h1 class="report-title">${displayTitle}</h1>
|
|
236
264
|
<div class="report-actions">
|
|
265
|
+
<button
|
|
266
|
+
type="button"
|
|
267
|
+
class="btn-refresh"
|
|
268
|
+
title="Refresh"
|
|
269
|
+
aria-label="Refresh data"
|
|
270
|
+
@click=${this._onRefresh}
|
|
271
|
+
>
|
|
272
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
|
273
|
+
<path d="M21 12a9 9 0 0 0-9-9 9.75 9.75 0 0 0-6.74 2.74L3 8" />
|
|
274
|
+
<path d="M3 3v5h5" />
|
|
275
|
+
<path d="M3 12a9 9 0 0 0 9 9 9.75 9.75 0 0 0 6.74-2.74L21 16" />
|
|
276
|
+
<path d="M16 21h5v-5" />
|
|
277
|
+
</svg>
|
|
278
|
+
</button>
|
|
237
279
|
<button type="button" @click=${this._print}>Print</button>
|
|
238
280
|
<a href=${this._pdfUrl} target="_blank" rel="noopener" download>Download PDF</a>
|
|
239
281
|
<a href=${this._excelUrl} target="_blank" rel="noopener" download>Download Excel</a>
|
|
@@ -253,8 +295,9 @@ let AnalyticsReport = class AnalyticsReport extends LitElement {
|
|
|
253
295
|
<div class="report-table-wrap">
|
|
254
296
|
<analytics-table
|
|
255
297
|
data-url=${this._dataUrl}
|
|
256
|
-
.dataParams=${this._params}
|
|
298
|
+
.dataParams=${{ ...this._params, _refresh: this._refreshKey }}
|
|
257
299
|
.title=${''}
|
|
300
|
+
hide-refresh-button
|
|
258
301
|
></analytics-table>
|
|
259
302
|
</div>
|
|
260
303
|
</div>
|
|
@@ -300,6 +343,9 @@ __decorate([
|
|
|
300
343
|
__decorate([
|
|
301
344
|
state()
|
|
302
345
|
], AnalyticsReport.prototype, "_error", void 0);
|
|
346
|
+
__decorate([
|
|
347
|
+
state()
|
|
348
|
+
], AnalyticsReport.prototype, "_refreshKey", void 0);
|
|
303
349
|
AnalyticsReport = __decorate([
|
|
304
350
|
customElement('analytics-report')
|
|
305
351
|
], AnalyticsReport);
|
package/widgets/base-chart.d.ts
CHANGED
|
@@ -88,6 +88,7 @@ export declare abstract class BaseChartWidget extends LitElement {
|
|
|
88
88
|
type?: string;
|
|
89
89
|
}>, _widgetData?: unknown): EChartsOption;
|
|
90
90
|
private _onWidgetFilterChange;
|
|
91
|
+
private _onRefresh;
|
|
91
92
|
render(): import("lit-html").TemplateResult<1>;
|
|
92
93
|
}
|
|
93
94
|
//# sourceMappingURL=base-chart.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"base-chart.d.ts","sourceRoot":"","sources":["../../../../libs/analytics/src/widgets/base-chart.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAsB,MAAM,KAAK,CAAC;AAErD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAG/D,OAAO,KAAK,EAA0B,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAO5F,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,qCAAqC,CAAC;AAM9E,OAAO,qBAAqB,CAAC;AAE7B;;;;;GAKG;AACH,8BAAsB,eAAgB,SAAQ,UAAU;IACtD,OAAgB,MAAM,
|
|
1
|
+
{"version":3,"file":"base-chart.d.ts","sourceRoot":"","sources":["../../../../libs/analytics/src/widgets/base-chart.ts"],"names":[],"mappings":"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"}
|
package/widgets/base-chart.js
CHANGED
|
@@ -41,15 +41,122 @@ export class BaseChartWidget extends LitElement {
|
|
|
41
41
|
.loading-overlay {
|
|
42
42
|
position: absolute;
|
|
43
43
|
inset: 0;
|
|
44
|
-
z-index:
|
|
44
|
+
z-index: 10;
|
|
45
45
|
display: flex;
|
|
46
46
|
align-items: center;
|
|
47
47
|
justify-content: center;
|
|
48
|
-
background: rgba(255, 255, 255, 0.
|
|
48
|
+
background: rgba(255, 255, 255, 0.8);
|
|
49
49
|
backdrop-filter: blur(4px);
|
|
50
50
|
-webkit-backdrop-filter: blur(4px);
|
|
51
51
|
border-radius: 4px;
|
|
52
52
|
}
|
|
53
|
+
.skeleton-line-chart {
|
|
54
|
+
width: 100%;
|
|
55
|
+
height: 100%;
|
|
56
|
+
min-height: 120px;
|
|
57
|
+
display: flex;
|
|
58
|
+
align-items: center;
|
|
59
|
+
justify-content: center;
|
|
60
|
+
padding: 1rem;
|
|
61
|
+
box-sizing: border-box;
|
|
62
|
+
}
|
|
63
|
+
.skeleton-line-chart-canvas {
|
|
64
|
+
width: 70%;
|
|
65
|
+
max-width: 180px;
|
|
66
|
+
aspect-ratio: 1;
|
|
67
|
+
min-width: 0;
|
|
68
|
+
}
|
|
69
|
+
.skeleton-line-chart-canvas svg {
|
|
70
|
+
width: 100%;
|
|
71
|
+
height: 100%;
|
|
72
|
+
display: block;
|
|
73
|
+
}
|
|
74
|
+
.skeleton-line-chart .zigzag {
|
|
75
|
+
fill: none;
|
|
76
|
+
stroke: #e5e7eb;
|
|
77
|
+
stroke-width: 2.5;
|
|
78
|
+
stroke-linecap: round;
|
|
79
|
+
stroke-linejoin: round;
|
|
80
|
+
}
|
|
81
|
+
.skeleton-line-chart .zigzag-shine {
|
|
82
|
+
fill: none;
|
|
83
|
+
stroke: url(#skeleton-line-gradient);
|
|
84
|
+
stroke-width: 2.5;
|
|
85
|
+
stroke-linecap: round;
|
|
86
|
+
stroke-linejoin: round;
|
|
87
|
+
stroke-dasharray: 15 85;
|
|
88
|
+
animation: analytics-line-shimmer 1.2s ease-in-out infinite;
|
|
89
|
+
}
|
|
90
|
+
@keyframes analytics-line-shimmer {
|
|
91
|
+
to { stroke-dashoffset: -100; }
|
|
92
|
+
}
|
|
93
|
+
.skeleton-bar-chart {
|
|
94
|
+
width: 100%;
|
|
95
|
+
height: 100%;
|
|
96
|
+
min-height: 120px;
|
|
97
|
+
display: flex;
|
|
98
|
+
align-items: center;
|
|
99
|
+
justify-content: center;
|
|
100
|
+
padding: 1rem;
|
|
101
|
+
box-sizing: border-box;
|
|
102
|
+
}
|
|
103
|
+
.skeleton-bar-chart-canvas {
|
|
104
|
+
width: 70%;
|
|
105
|
+
max-width: 180px;
|
|
106
|
+
aspect-ratio: 1;
|
|
107
|
+
display: flex;
|
|
108
|
+
align-items: flex-end;
|
|
109
|
+
gap: 1rem;
|
|
110
|
+
min-width: 0;
|
|
111
|
+
}
|
|
112
|
+
.skeleton-bar-vert {
|
|
113
|
+
flex: 1;
|
|
114
|
+
min-width: 0;
|
|
115
|
+
border-radius: 4px 4px 0 0;
|
|
116
|
+
background: linear-gradient(
|
|
117
|
+
90deg,
|
|
118
|
+
#e5e7eb 0%,
|
|
119
|
+
#e5e7eb 40%,
|
|
120
|
+
#f3f4f6 50%,
|
|
121
|
+
#e5e7eb 60%,
|
|
122
|
+
#e5e7eb 100%
|
|
123
|
+
);
|
|
124
|
+
background-size: 200% 100%;
|
|
125
|
+
animation: analytics-skeleton-shimmer 1.2s ease-in-out infinite;
|
|
126
|
+
}
|
|
127
|
+
.skeleton-bar-vert:nth-child(1) { height: 55%; }
|
|
128
|
+
.skeleton-bar-vert:nth-child(2) { height: 70%; }
|
|
129
|
+
.skeleton-bar-vert:nth-child(3) { height: 60%; }
|
|
130
|
+
.skeleton-bar-vert:nth-child(4) { height: 80%; }
|
|
131
|
+
.skeleton-pie {
|
|
132
|
+
width: 100%;
|
|
133
|
+
height: 100%;
|
|
134
|
+
min-height: 120px;
|
|
135
|
+
display: flex;
|
|
136
|
+
align-items: center;
|
|
137
|
+
justify-content: center;
|
|
138
|
+
padding: 1rem;
|
|
139
|
+
box-sizing: border-box;
|
|
140
|
+
}
|
|
141
|
+
.skeleton-circle {
|
|
142
|
+
width: 70%;
|
|
143
|
+
max-width: 180px;
|
|
144
|
+
aspect-ratio: 1;
|
|
145
|
+
border-radius: 50%;
|
|
146
|
+
background: linear-gradient(
|
|
147
|
+
90deg,
|
|
148
|
+
#e5e7eb 0%,
|
|
149
|
+
#e5e7eb 40%,
|
|
150
|
+
#f3f4f6 50%,
|
|
151
|
+
#e5e7eb 60%,
|
|
152
|
+
#e5e7eb 100%
|
|
153
|
+
);
|
|
154
|
+
background-size: 200% 100%;
|
|
155
|
+
animation: analytics-skeleton-shimmer 1.2s ease-in-out infinite;
|
|
156
|
+
}
|
|
157
|
+
@keyframes analytics-skeleton-shimmer {
|
|
158
|
+
to { background-position: 200% 0; }
|
|
159
|
+
}
|
|
53
160
|
.loading-spinner {
|
|
54
161
|
width: 2rem;
|
|
55
162
|
height: 2rem;
|
|
@@ -68,6 +175,7 @@ export class BaseChartWidget extends LitElement {
|
|
|
68
175
|
min-height: 120px;
|
|
69
176
|
display: flex;
|
|
70
177
|
flex-direction: column;
|
|
178
|
+
--analytics-widget-toolbar-top: 2.5rem;
|
|
71
179
|
}
|
|
72
180
|
.widget-with-toolbar:hover analytics-widget-toolbar {
|
|
73
181
|
opacity: 1;
|
|
@@ -77,12 +185,18 @@ export class BaseChartWidget extends LitElement {
|
|
|
77
185
|
flex-shrink: 0;
|
|
78
186
|
padding: 0.5rem 0.75rem 0.25rem 0.75rem;
|
|
79
187
|
min-width: 0;
|
|
188
|
+
display: flex;
|
|
189
|
+
align-items: center;
|
|
190
|
+
justify-content: space-between;
|
|
191
|
+
gap: 0.5rem;
|
|
192
|
+
background: var(--analytics-widget-header-bg, #fff);
|
|
193
|
+
color: var(--analytics-widget-header-color, #374151);
|
|
80
194
|
}
|
|
81
195
|
.widget-title {
|
|
82
196
|
margin: 0;
|
|
83
197
|
font-size: 0.9375rem;
|
|
84
198
|
font-weight: 600;
|
|
85
|
-
color: #374151;
|
|
199
|
+
color: var(--analytics-widget-header-color, #374151);
|
|
86
200
|
line-height: 1.3;
|
|
87
201
|
display: -webkit-box;
|
|
88
202
|
-webkit-box-orient: vertical;
|
|
@@ -90,6 +204,22 @@ export class BaseChartWidget extends LitElement {
|
|
|
90
204
|
overflow: hidden;
|
|
91
205
|
text-overflow: ellipsis;
|
|
92
206
|
}
|
|
207
|
+
.skeleton-title {
|
|
208
|
+
height: 1rem;
|
|
209
|
+
width: 8rem;
|
|
210
|
+
max-width: 60%;
|
|
211
|
+
border-radius: 4px;
|
|
212
|
+
background: linear-gradient(
|
|
213
|
+
90deg,
|
|
214
|
+
#e5e7eb 0%,
|
|
215
|
+
#e5e7eb 40%,
|
|
216
|
+
#f3f4f6 50%,
|
|
217
|
+
#e5e7eb 60%,
|
|
218
|
+
#e5e7eb 100%
|
|
219
|
+
);
|
|
220
|
+
background-size: 200% 100%;
|
|
221
|
+
animation: analytics-skeleton-shimmer 1.2s ease-in-out infinite;
|
|
222
|
+
}
|
|
93
223
|
.widget-body {
|
|
94
224
|
flex: 1;
|
|
95
225
|
min-height: 0;
|
|
@@ -100,6 +230,33 @@ export class BaseChartWidget extends LitElement {
|
|
|
100
230
|
height: 100%;
|
|
101
231
|
min-height: 100px;
|
|
102
232
|
}
|
|
233
|
+
.btn-refresh {
|
|
234
|
+
flex-shrink: 0;
|
|
235
|
+
display: inline-flex;
|
|
236
|
+
align-items: center;
|
|
237
|
+
justify-content: center;
|
|
238
|
+
width: 1.75rem;
|
|
239
|
+
height: 1.75rem;
|
|
240
|
+
padding: 0;
|
|
241
|
+
border: none;
|
|
242
|
+
border-radius: 4px;
|
|
243
|
+
background: transparent;
|
|
244
|
+
color: var(--analytics-widget-header-color, #6b7280);
|
|
245
|
+
cursor: pointer;
|
|
246
|
+
transition: color 0.15s ease, background-color 0.15s ease;
|
|
247
|
+
}
|
|
248
|
+
.btn-refresh:hover {
|
|
249
|
+
color: var(--analytics-widget-header-color, #374151);
|
|
250
|
+
background: #f3f4f6;
|
|
251
|
+
}
|
|
252
|
+
.btn-refresh:disabled {
|
|
253
|
+
cursor: not-allowed;
|
|
254
|
+
opacity: 0.6;
|
|
255
|
+
}
|
|
256
|
+
.btn-refresh svg {
|
|
257
|
+
width: 1rem;
|
|
258
|
+
height: 1rem;
|
|
259
|
+
}
|
|
103
260
|
`; }
|
|
104
261
|
/** Delay (ms) after last resize event before calling chart.resize(). */
|
|
105
262
|
static { this._RESIZE_DELAY_MS = 300; }
|
|
@@ -241,10 +398,8 @@ export class BaseChartWidget extends LitElement {
|
|
|
241
398
|
this._viewportUnobserve = observeViewport(this, { rootMargin: this.prefetchMargin || '200px', threshold: 0 }, {
|
|
242
399
|
onEnter: () => {
|
|
243
400
|
this._viewportVisible = true;
|
|
244
|
-
if (!this._hasLoadedOnce)
|
|
245
|
-
this._hasLoadedOnce = true;
|
|
401
|
+
if (!this._hasLoadedOnce)
|
|
246
402
|
this._loadData();
|
|
247
|
-
}
|
|
248
403
|
},
|
|
249
404
|
onLeave: () => {
|
|
250
405
|
this._viewportVisible = false;
|
|
@@ -355,6 +510,7 @@ export class BaseChartWidget extends LitElement {
|
|
|
355
510
|
this._widgetFilters = filters;
|
|
356
511
|
if (hideFilter !== undefined)
|
|
357
512
|
this._hideFilter = hideFilter;
|
|
513
|
+
this._hasLoadedOnce = true;
|
|
358
514
|
this._loading = false;
|
|
359
515
|
this._renderChart();
|
|
360
516
|
});
|
|
@@ -372,6 +528,7 @@ export class BaseChartWidget extends LitElement {
|
|
|
372
528
|
this.data = data;
|
|
373
529
|
this.meta = meta;
|
|
374
530
|
this._widgetData = null;
|
|
531
|
+
this._hasLoadedOnce = true;
|
|
375
532
|
this._loading = false;
|
|
376
533
|
this._renderChart();
|
|
377
534
|
});
|
|
@@ -427,24 +584,103 @@ export class BaseChartWidget extends LitElement {
|
|
|
427
584
|
_onWidgetFilterChange(e) {
|
|
428
585
|
this._widgetFilterParams = { ...(e.detail?.params ?? {}) };
|
|
429
586
|
}
|
|
587
|
+
_onRefresh() {
|
|
588
|
+
if (this._loading)
|
|
589
|
+
return;
|
|
590
|
+
this._loadData();
|
|
591
|
+
}
|
|
430
592
|
render() {
|
|
431
593
|
const hasWidgetFilters = this._widgetFilters.length > 0 && !this._hideFilter;
|
|
432
594
|
const widgetActiveCount = Object.keys(this._widgetFilterParams).filter((k) => this._widgetFilterParams[k] !== '' && this._widgetFilterParams[k] != null).length;
|
|
595
|
+
const canRefresh = Boolean(this._getEffectiveDataUrl());
|
|
596
|
+
const showSkeleton = this._loading && !this._hasLoadedOnce;
|
|
597
|
+
const showTitlePlaceholder = !this._effectiveTitle;
|
|
598
|
+
const headerContent = this._effectiveTitle || canRefresh || showTitlePlaceholder
|
|
599
|
+
? html `
|
|
600
|
+
<header class="widget-header">
|
|
601
|
+
${this._effectiveTitle
|
|
602
|
+
? html `<h2 class="widget-title">${this._effectiveTitle}</h2>`
|
|
603
|
+
: showTitlePlaceholder
|
|
604
|
+
? html `<div class="skeleton-title" aria-hidden="true"></div>`
|
|
605
|
+
: html `<span></span>`}
|
|
606
|
+
${canRefresh
|
|
607
|
+
? html `
|
|
608
|
+
<button
|
|
609
|
+
type="button"
|
|
610
|
+
class="btn-refresh"
|
|
611
|
+
title="Refresh"
|
|
612
|
+
aria-label="Refresh data"
|
|
613
|
+
?disabled=${this._loading}
|
|
614
|
+
@click=${this._onRefresh}
|
|
615
|
+
>
|
|
616
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
|
617
|
+
<path d="M21 12a9 9 0 0 0-9-9 9.75 9.75 0 0 0-6.74 2.74L3 8" />
|
|
618
|
+
<path d="M3 3v5h5" />
|
|
619
|
+
<path d="M3 12a9 9 0 0 0 9 9 9.75 9.75 0 0 0 6.74-2.74L21 16" />
|
|
620
|
+
<path d="M16 21h5v-5" />
|
|
621
|
+
</svg>
|
|
622
|
+
</button>
|
|
623
|
+
`
|
|
624
|
+
: ''}
|
|
625
|
+
</header>
|
|
626
|
+
`
|
|
627
|
+
: '';
|
|
628
|
+
const showSpinnerOverlay = this._loading && this._hasLoadedOnce;
|
|
629
|
+
const isBarChart = this.tagName === 'ANALYTICS-BAR-CHART';
|
|
630
|
+
const isPieChart = this.tagName === 'ANALYTICS-PIE-CHART';
|
|
631
|
+
const skeletonContent = showSkeleton
|
|
632
|
+
? isBarChart
|
|
633
|
+
? html `
|
|
634
|
+
<div class="skeleton-bar-chart" aria-busy="true" aria-live="polite">
|
|
635
|
+
<div class="skeleton-bar-chart-canvas">
|
|
636
|
+
<div class="skeleton-bar-vert"></div>
|
|
637
|
+
<div class="skeleton-bar-vert"></div>
|
|
638
|
+
<div class="skeleton-bar-vert"></div>
|
|
639
|
+
<div class="skeleton-bar-vert"></div>
|
|
640
|
+
</div>
|
|
641
|
+
</div>
|
|
642
|
+
`
|
|
643
|
+
: isPieChart
|
|
644
|
+
? html `
|
|
645
|
+
<div class="skeleton-pie" aria-busy="true" aria-live="polite">
|
|
646
|
+
<div class="skeleton-circle"></div>
|
|
647
|
+
</div>
|
|
648
|
+
`
|
|
649
|
+
: html `
|
|
650
|
+
<div class="skeleton-line-chart" aria-busy="true" aria-live="polite">
|
|
651
|
+
<div class="skeleton-line-chart-canvas">
|
|
652
|
+
<svg viewBox="0 0 100 100" preserveAspectRatio="none">
|
|
653
|
+
<defs>
|
|
654
|
+
<linearGradient id="skeleton-line-gradient" x1="0%" y1="0%" x2="100%" y2="0%">
|
|
655
|
+
<stop offset="0%" stop-color="#e5e7eb"/>
|
|
656
|
+
<stop offset="40%" stop-color="#e5e7eb"/>
|
|
657
|
+
<stop offset="50%" stop-color="#f3f4f6"/>
|
|
658
|
+
<stop offset="60%" stop-color="#e5e7eb"/>
|
|
659
|
+
<stop offset="100%" stop-color="#e5e7eb"/>
|
|
660
|
+
</linearGradient>
|
|
661
|
+
</defs>
|
|
662
|
+
<polyline class="zigzag" points="8,80 23,29 38,33 53,37 68,90 83,82 95,10"/>
|
|
663
|
+
<polyline class="zigzag zigzag-shine" points="8,80 23,29 38,33 53,37 68,90 83,82 95,10"/>
|
|
664
|
+
</svg>
|
|
665
|
+
</div>
|
|
666
|
+
</div>
|
|
667
|
+
`
|
|
668
|
+
: null;
|
|
433
669
|
return html `
|
|
434
670
|
<div class="widget-with-toolbar">
|
|
435
|
-
${
|
|
436
|
-
? html `<header class="widget-header"><h2 class="widget-title">${this._effectiveTitle}</h2></header>`
|
|
437
|
-
: ''}
|
|
671
|
+
${headerContent}
|
|
438
672
|
<div class="widget-body">
|
|
439
|
-
${
|
|
440
|
-
?
|
|
441
|
-
:
|
|
442
|
-
|
|
673
|
+
${skeletonContent !== null
|
|
674
|
+
? skeletonContent
|
|
675
|
+
: this._error
|
|
676
|
+
? html `<div class="error">${this._error}</div>`
|
|
677
|
+
: html `<div class="chart"></div>`}
|
|
678
|
+
</div>
|
|
679
|
+
${showSpinnerOverlay
|
|
443
680
|
? html `<div class="loading-overlay" aria-busy="true" aria-live="polite">
|
|
444
|
-
|
|
445
|
-
|
|
681
|
+
<div class="loading-spinner" aria-hidden="true"></div>
|
|
682
|
+
</div>`
|
|
446
683
|
: nothing}
|
|
447
|
-
</div>
|
|
448
684
|
${hasWidgetFilters
|
|
449
685
|
? html `
|
|
450
686
|
<analytics-widget-toolbar
|
package/widgets/table.d.ts
CHANGED
|
@@ -12,6 +12,8 @@ export declare class TableWidget extends LitElement {
|
|
|
12
12
|
static styles: import("lit").CSSResult;
|
|
13
13
|
/** Optional title override. When unset, the widget uses the title from the API definition. */
|
|
14
14
|
title: string;
|
|
15
|
+
/** When true, the header refresh button is hidden (e.g. when table is embedded in analytics-report which has its own refresh). */
|
|
16
|
+
hideRefreshButton: boolean;
|
|
15
17
|
/** API URL to fetch data (e.g. /api/analytics/widgets/1/data). When widgetId + apiUrl are set, overridden by buildWidgetDataUrl(apiUrl, widgetId). */
|
|
16
18
|
dataUrl: string;
|
|
17
19
|
/** Widget id (used with apiUrl to build data URL: /widgets/{id}/data). */
|
|
@@ -59,6 +61,7 @@ export declare class TableWidget extends LitElement {
|
|
|
59
61
|
private _viewportUnobserve;
|
|
60
62
|
constructor();
|
|
61
63
|
private _boundOnDashboardFilterChange;
|
|
64
|
+
private _onRefresh;
|
|
62
65
|
private _boundOnDashboardRefresh;
|
|
63
66
|
connectedCallback(): void;
|
|
64
67
|
disconnectedCallback(): void;
|
package/widgets/table.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"table.d.ts","sourceRoot":"","sources":["../../../../libs/analytics/src/widgets/table.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAsB,MAAM,KAAK,CAAC;AAGrD,OAAO,KAAK,EAA0B,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAM5F,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,qCAAqC,CAAC;AAM9E,OAAO,qBAAqB,CAAC;AAE7B;;;;;GAKG;AACH,qBACa,WAAY,SAAQ,UAAU;IACzC,OAAgB,MAAM,
|
|
1
|
+
{"version":3,"file":"table.d.ts","sourceRoot":"","sources":["../../../../libs/analytics/src/widgets/table.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAsB,MAAM,KAAK,CAAC;AAGrD,OAAO,KAAK,EAA0B,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAM5F,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,qCAAqC,CAAC;AAM9E,OAAO,qBAAqB,CAAC;AAE7B;;;;;GAKG;AACH,qBACa,WAAY,SAAQ,UAAU;IACzC,OAAgB,MAAM,0BAuNpB;IAEF,8FAA8F;IAC1D,KAAK,EAAE,MAAM,CAAC;IAElD,kIAAkI;IAC3D,iBAAiB,EAAE,OAAO,CAAC;IAElG,sJAAsJ;IAC3F,OAAO,EAAE,MAAM,CAAC;IAE3E,0EAA0E;IAC1E,qFAAqF;IACzB,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAC;IAEtF,+FAA+F;IACrC,MAAM,EAAE,MAAM,CAAC;IAEzE,iHAAiH;IAC7E,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,CAAC;IAE1F,2GAA2G;IACvE,SAAS,EAAE,kBAAkB,GAAG,IAAI,CAAC;IAEzE,8DAA8D;IAC1B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,IAAI,CAAC;IAE3E,oDAAoD;IAChB,IAAI,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE;YAAE,IAAI,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC,CAAA;KAAE,CAAC;IAEzF,qKAAqK;IACrK,QAAyB,WAAW,CAAyB;IAE7D,oGAAoG;IAC/D,IAAI,EAAE,OAAO,CAAC;IAEnD,uGAAuG;IACrC,cAAc,EAAE,MAAM,CAAC;IAEzF,yHAAyH;IACrF,iBAAiB,EAAE,sBAAsB,GAAG,SAAS,CAAC;IAE1F,QAAyB,QAAQ,CAAU;IAC3C,QAAyB,MAAM,CAAgB;IAC/C,OAAO,CAAC,eAAe,CAAK;IAC5B,OAAO,CAAC,mBAAmB,CAA8C;IACzE,OAAO,CAAC,kBAAkB,CAAS;IACnC,OAAO,CAAC,iBAAiB,CAAS;IAClC,OAAO,CAAC,uBAAuB,CAAS;IACxC,QAAyB,gBAAgB,CAA4C;IACrF,QAAyB,cAAc,CAAqB;IAC5D,QAAyB,mBAAmB,CAA4C;IACxF,QAAyB,WAAW,CAAU;IAC9C,OAAO,CAAC,YAAY,CAAmC;IACvD,OAAO,CAAC,uBAAuB,CAAS;IACxC,QAAyB,gBAAgB,CAAU;IACnD,qFAAqF;IACrF,QAAyB,gBAAgB,CAAS;IAClD,OAAO,CAAC,cAAc,CAAS;IAC/B,OAAO,CAAC,kBAAkB,CAA6B;;IAyBvD,OAAO,CAAC,6BAA6B,CAInC;IACF,OAAO,CAAC,UAAU;IAKlB,OAAO,CAAC,wBAAwB,CAG9B;IAEO,iBAAiB,IAAI,IAAI;IAKzB,oBAAoB,IAAI,IAAI;IAW5B,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAuCrD,OAAO,CAAC,gBAAgB;IASxB,OAAO,CAAC,gBAAgB;IASf,YAAY,IAAI,IAAI;IAkB7B,sHAAsH;IACtH,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,sBAAsB,CAAM;IAEpD,yGAAyG;IACzG,OAAO,CAAC,iBAAiB;IAWzB,OAAO,CAAC,oBAAoB;IAO5B,OAAO,CAAC,mBAAmB;IA2B3B,gFAAgF;IAChF,OAAO,KAAK,eAAe,GAE1B;IAED,6FAA6F;IAC7F,OAAO,CAAC,MAAM;YAQA,SAAS;IAsIvB,OAAO,CAAC,qBAAqB;IAM7B,+FAA+F;IAC/F,OAAO,CAAC,WAAW;IASnB,oEAAoE;IACpE,OAAO,CAAC,QAAQ;IAMhB,2FAA2F;IAC3F,OAAO,CAAC,eAAe;IASvB,sEAAsE;IACtE,OAAO,CAAC,eAAe;IASvB,kFAAkF;IAClF,OAAO,CAAC,eAAe;IAOvB,OAAO,CAAC,YAAY;IAIX,MAAM;IAsIf,OAAO,CAAC,YAAY;IA0BpB,OAAO,CAAC,cAAc;IAmCtB,OAAO,CAAC,WAAW;CAKpB"}
|
package/widgets/table.js
CHANGED
|
@@ -20,7 +20,6 @@ let TableWidget = class TableWidget extends LitElement {
|
|
|
20
20
|
flex-direction: column;
|
|
21
21
|
width: 100%;
|
|
22
22
|
height: 100%;
|
|
23
|
-
overflow: hidden;
|
|
24
23
|
}
|
|
25
24
|
table {
|
|
26
25
|
width: 100%;
|
|
@@ -76,15 +75,54 @@ let TableWidget = class TableWidget extends LitElement {
|
|
|
76
75
|
.loading-overlay {
|
|
77
76
|
position: absolute;
|
|
78
77
|
inset: 0;
|
|
79
|
-
z-index:
|
|
78
|
+
z-index: 10;
|
|
80
79
|
display: flex;
|
|
81
80
|
align-items: center;
|
|
82
81
|
justify-content: center;
|
|
83
|
-
background: rgba(255, 255, 255, 0.
|
|
82
|
+
background: rgba(255, 255, 255, 0.8);
|
|
84
83
|
backdrop-filter: blur(4px);
|
|
85
84
|
-webkit-backdrop-filter: blur(4px);
|
|
86
85
|
border-radius: 4px;
|
|
87
86
|
}
|
|
87
|
+
.skeleton {
|
|
88
|
+
width: 100%;
|
|
89
|
+
padding: 0.75rem 1rem;
|
|
90
|
+
box-sizing: border-box;
|
|
91
|
+
}
|
|
92
|
+
.skeleton-row {
|
|
93
|
+
display: flex;
|
|
94
|
+
gap: 0.75rem;
|
|
95
|
+
align-items: center;
|
|
96
|
+
padding: 0.5rem 0;
|
|
97
|
+
border-bottom: 1px solid #f3f4f6;
|
|
98
|
+
}
|
|
99
|
+
.skeleton-cell {
|
|
100
|
+
height: 1rem;
|
|
101
|
+
border-radius: 4px;
|
|
102
|
+
background: linear-gradient(
|
|
103
|
+
90deg,
|
|
104
|
+
#e5e7eb 0%,
|
|
105
|
+
#e5e7eb 40%,
|
|
106
|
+
#f3f4f6 50%,
|
|
107
|
+
#e5e7eb 60%,
|
|
108
|
+
#e5e7eb 100%
|
|
109
|
+
);
|
|
110
|
+
background-size: 200% 100%;
|
|
111
|
+
animation: analytics-table-skeleton-shimmer 1.2s ease-in-out infinite;
|
|
112
|
+
}
|
|
113
|
+
.skeleton-row:first-child .skeleton-cell {
|
|
114
|
+
height: 1.25rem;
|
|
115
|
+
background: #f3f4f6;
|
|
116
|
+
}
|
|
117
|
+
.skeleton-row:first-child .skeleton-cell:nth-child(1) { flex: 1.2; }
|
|
118
|
+
.skeleton-row:first-child .skeleton-cell:nth-child(2) { flex: 1; }
|
|
119
|
+
.skeleton-row:first-child .skeleton-cell:nth-child(3) { flex: 0.8; }
|
|
120
|
+
.skeleton-row:not(:first-child) .skeleton-cell:nth-child(1) { flex: 1.2; min-width: 0; }
|
|
121
|
+
.skeleton-row:not(:first-child) .skeleton-cell:nth-child(2) { flex: 1; min-width: 0; }
|
|
122
|
+
.skeleton-row:not(:first-child) .skeleton-cell:nth-child(3) { flex: 0.8; min-width: 0; }
|
|
123
|
+
@keyframes analytics-table-skeleton-shimmer {
|
|
124
|
+
to { background-position: 200% 0; }
|
|
125
|
+
}
|
|
88
126
|
.loading-spinner {
|
|
89
127
|
width: 2rem;
|
|
90
128
|
height: 2rem;
|
|
@@ -105,6 +143,33 @@ let TableWidget = class TableWidget extends LitElement {
|
|
|
105
143
|
padding: 2rem 0.75rem;
|
|
106
144
|
color: #9ca3af;
|
|
107
145
|
}
|
|
146
|
+
.btn-refresh {
|
|
147
|
+
flex-shrink: 0;
|
|
148
|
+
display: inline-flex;
|
|
149
|
+
align-items: center;
|
|
150
|
+
justify-content: center;
|
|
151
|
+
width: 1.75rem;
|
|
152
|
+
height: 1.75rem;
|
|
153
|
+
padding: 0;
|
|
154
|
+
border: none;
|
|
155
|
+
border-radius: 4px;
|
|
156
|
+
background: transparent;
|
|
157
|
+
color: var(--analytics-widget-header-color, #6b7280);
|
|
158
|
+
cursor: pointer;
|
|
159
|
+
transition: color 0.15s ease, background-color 0.15s ease;
|
|
160
|
+
}
|
|
161
|
+
.btn-refresh:hover {
|
|
162
|
+
color: var(--analytics-widget-header-color, #374151);
|
|
163
|
+
background: #f3f4f6;
|
|
164
|
+
}
|
|
165
|
+
.btn-refresh:disabled {
|
|
166
|
+
cursor: not-allowed;
|
|
167
|
+
opacity: 0.6;
|
|
168
|
+
}
|
|
169
|
+
.btn-refresh svg {
|
|
170
|
+
width: 1rem;
|
|
171
|
+
height: 1rem;
|
|
172
|
+
}
|
|
108
173
|
.widget-with-toolbar {
|
|
109
174
|
position: relative;
|
|
110
175
|
width: 100%;
|
|
@@ -112,6 +177,7 @@ let TableWidget = class TableWidget extends LitElement {
|
|
|
112
177
|
display: flex;
|
|
113
178
|
flex-direction: column;
|
|
114
179
|
min-height: 0;
|
|
180
|
+
--analytics-widget-toolbar-top: 2.5rem;
|
|
115
181
|
}
|
|
116
182
|
.widget-with-toolbar:hover analytics-widget-toolbar {
|
|
117
183
|
opacity: 1;
|
|
@@ -121,12 +187,18 @@ let TableWidget = class TableWidget extends LitElement {
|
|
|
121
187
|
flex-shrink: 0;
|
|
122
188
|
padding: 0.5rem 0.75rem 0.25rem 0.75rem;
|
|
123
189
|
min-width: 0;
|
|
190
|
+
display: flex;
|
|
191
|
+
align-items: center;
|
|
192
|
+
justify-content: space-between;
|
|
193
|
+
gap: 0.5rem;
|
|
194
|
+
background: var(--analytics-widget-header-bg, #fff);
|
|
195
|
+
color: var(--analytics-widget-header-color, #374151);
|
|
124
196
|
}
|
|
125
197
|
.widget-title {
|
|
126
198
|
margin: 0;
|
|
127
199
|
font-size: 0.9375rem;
|
|
128
200
|
font-weight: 600;
|
|
129
|
-
color: #374151;
|
|
201
|
+
color: var(--analytics-widget-header-color, #374151);
|
|
130
202
|
line-height: 1.3;
|
|
131
203
|
display: -webkit-box;
|
|
132
204
|
-webkit-box-orient: vertical;
|
|
@@ -134,6 +206,22 @@ let TableWidget = class TableWidget extends LitElement {
|
|
|
134
206
|
overflow: hidden;
|
|
135
207
|
text-overflow: ellipsis;
|
|
136
208
|
}
|
|
209
|
+
.skeleton-title {
|
|
210
|
+
height: 1rem;
|
|
211
|
+
width: 8rem;
|
|
212
|
+
max-width: 60%;
|
|
213
|
+
border-radius: 4px;
|
|
214
|
+
background: linear-gradient(
|
|
215
|
+
90deg,
|
|
216
|
+
#e5e7eb 0%,
|
|
217
|
+
#e5e7eb 40%,
|
|
218
|
+
#f3f4f6 50%,
|
|
219
|
+
#e5e7eb 60%,
|
|
220
|
+
#e5e7eb 100%
|
|
221
|
+
);
|
|
222
|
+
background-size: 200% 100%;
|
|
223
|
+
animation: analytics-table-skeleton-shimmer 1.2s ease-in-out infinite;
|
|
224
|
+
}
|
|
137
225
|
.widget-body {
|
|
138
226
|
flex: 1;
|
|
139
227
|
min-height: 0;
|
|
@@ -184,6 +272,11 @@ let TableWidget = class TableWidget extends LitElement {
|
|
|
184
272
|
this._viewportVisible = true;
|
|
185
273
|
this._definitionTitle = '';
|
|
186
274
|
}
|
|
275
|
+
_onRefresh() {
|
|
276
|
+
if (this._loading)
|
|
277
|
+
return;
|
|
278
|
+
this._loadData();
|
|
279
|
+
}
|
|
187
280
|
connectedCallback() {
|
|
188
281
|
super.connectedCallback();
|
|
189
282
|
this._attachDashboard();
|
|
@@ -261,10 +354,8 @@ let TableWidget = class TableWidget extends LitElement {
|
|
|
261
354
|
}, {
|
|
262
355
|
onEnter: () => {
|
|
263
356
|
this._viewportVisible = true;
|
|
264
|
-
if (!this._hasLoadedOnce)
|
|
265
|
-
this._hasLoadedOnce = true;
|
|
357
|
+
if (!this._hasLoadedOnce)
|
|
266
358
|
this._loadData();
|
|
267
|
-
}
|
|
268
359
|
},
|
|
269
360
|
onLeave: () => {
|
|
270
361
|
this._viewportVisible = false;
|
|
@@ -408,6 +499,7 @@ let TableWidget = class TableWidget extends LitElement {
|
|
|
408
499
|
this._widgetFilters = [];
|
|
409
500
|
if (hideFilter !== undefined)
|
|
410
501
|
this._hideFilter = hideFilter;
|
|
502
|
+
this._hasLoadedOnce = true;
|
|
411
503
|
this._loading = false;
|
|
412
504
|
if (this._loadDataPendingRefresh) {
|
|
413
505
|
this._loadDataPendingRefresh = false;
|
|
@@ -451,6 +543,7 @@ let TableWidget = class TableWidget extends LitElement {
|
|
|
451
543
|
this._widgetFilters = [];
|
|
452
544
|
if (hideFilter !== undefined)
|
|
453
545
|
this._hideFilter = hideFilter;
|
|
546
|
+
this._hasLoadedOnce = true;
|
|
454
547
|
this._loading = false;
|
|
455
548
|
if (this._loadDataPendingRefresh) {
|
|
456
549
|
this._loadDataPendingRefresh = false;
|
|
@@ -538,6 +631,39 @@ let TableWidget = class TableWidget extends LitElement {
|
|
|
538
631
|
const wd = this._widgetData;
|
|
539
632
|
const hasHeaderRows = Boolean(wd?.header_rows?.length);
|
|
540
633
|
const tableFontSizeStyle = wd?.font_size ? `font-size: ${wd.font_size}` : nothing;
|
|
634
|
+
const canRefresh = Boolean(this._getEffectiveDataUrl()) && !this.hideRefreshButton;
|
|
635
|
+
const showSkeleton = this._loading && !this._hasLoadedOnce;
|
|
636
|
+
const showTitlePlaceholder = !this._effectiveTitle && !this.hideRefreshButton;
|
|
637
|
+
const headerContent = this._effectiveTitle || canRefresh || showTitlePlaceholder
|
|
638
|
+
? html `
|
|
639
|
+
<header class="widget-header">
|
|
640
|
+
${this._effectiveTitle
|
|
641
|
+
? html `<h2 class="widget-title">${this._effectiveTitle}</h2>`
|
|
642
|
+
: showTitlePlaceholder
|
|
643
|
+
? html `<div class="skeleton-title" aria-hidden="true"></div>`
|
|
644
|
+
: html `<span></span>`}
|
|
645
|
+
${canRefresh
|
|
646
|
+
? html `
|
|
647
|
+
<button
|
|
648
|
+
type="button"
|
|
649
|
+
class="btn-refresh"
|
|
650
|
+
title="Refresh"
|
|
651
|
+
aria-label="Refresh data"
|
|
652
|
+
?disabled=${this._loading}
|
|
653
|
+
@click=${this._onRefresh}
|
|
654
|
+
>
|
|
655
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
|
656
|
+
<path d="M21 12a9 9 0 0 0-9-9 9.75 9.75 0 0 0-6.74 2.74L3 8" />
|
|
657
|
+
<path d="M3 3v5h5" />
|
|
658
|
+
<path d="M3 12a9 9 0 0 0 9 9 9.75 9.75 0 0 0 6.74-2.74L21 16" />
|
|
659
|
+
<path d="M16 21h5v-5" />
|
|
660
|
+
</svg>
|
|
661
|
+
</button>
|
|
662
|
+
`
|
|
663
|
+
: ''}
|
|
664
|
+
</header>
|
|
665
|
+
`
|
|
666
|
+
: '';
|
|
541
667
|
const filterToolbar = hasWidgetFilters
|
|
542
668
|
? html `
|
|
543
669
|
<analytics-widget-toolbar
|
|
@@ -548,12 +674,43 @@ let TableWidget = class TableWidget extends LitElement {
|
|
|
548
674
|
></analytics-widget-toolbar>
|
|
549
675
|
`
|
|
550
676
|
: html ``;
|
|
551
|
-
const
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
677
|
+
const showSpinnerOverlay = this._loading && this._hasLoadedOnce;
|
|
678
|
+
const bodyContent = showSkeleton
|
|
679
|
+
? html `
|
|
680
|
+
<div class="skeleton" aria-busy="true" aria-live="polite">
|
|
681
|
+
<div class="skeleton-row">
|
|
682
|
+
<div class="skeleton-cell"></div>
|
|
683
|
+
<div class="skeleton-cell"></div>
|
|
684
|
+
<div class="skeleton-cell"></div>
|
|
685
|
+
</div>
|
|
686
|
+
<div class="skeleton-row">
|
|
687
|
+
<div class="skeleton-cell"></div>
|
|
688
|
+
<div class="skeleton-cell"></div>
|
|
689
|
+
<div class="skeleton-cell"></div>
|
|
690
|
+
</div>
|
|
691
|
+
<div class="skeleton-row">
|
|
692
|
+
<div class="skeleton-cell"></div>
|
|
693
|
+
<div class="skeleton-cell"></div>
|
|
694
|
+
<div class="skeleton-cell"></div>
|
|
695
|
+
</div>
|
|
696
|
+
<div class="skeleton-row">
|
|
697
|
+
<div class="skeleton-cell"></div>
|
|
698
|
+
<div class="skeleton-cell"></div>
|
|
699
|
+
<div class="skeleton-cell"></div>
|
|
700
|
+
</div>
|
|
701
|
+
<div class="skeleton-row">
|
|
702
|
+
<div class="skeleton-cell"></div>
|
|
703
|
+
<div class="skeleton-cell"></div>
|
|
704
|
+
<div class="skeleton-cell"></div>
|
|
705
|
+
</div>
|
|
706
|
+
</div>
|
|
707
|
+
`
|
|
708
|
+
: this._error
|
|
709
|
+
? html `<div class="error">${this._error}</div>`
|
|
710
|
+
: columns.length === 0
|
|
711
|
+
? html `<div class="empty">No columns</div>`
|
|
712
|
+
: rows.length === 0
|
|
713
|
+
? html `
|
|
557
714
|
<table style=${tableFontSizeStyle}>
|
|
558
715
|
<thead>
|
|
559
716
|
${this._renderThead(columns, hasHeaderRows)}
|
|
@@ -565,7 +722,7 @@ let TableWidget = class TableWidget extends LitElement {
|
|
|
565
722
|
</tbody>
|
|
566
723
|
</table>
|
|
567
724
|
`
|
|
568
|
-
|
|
725
|
+
: html `
|
|
569
726
|
<table style=${tableFontSizeStyle}>
|
|
570
727
|
<thead>
|
|
571
728
|
${this._renderThead(columns, hasHeaderRows)}
|
|
@@ -577,17 +734,15 @@ let TableWidget = class TableWidget extends LitElement {
|
|
|
577
734
|
`;
|
|
578
735
|
return html `
|
|
579
736
|
<div class="widget-with-toolbar">
|
|
580
|
-
${
|
|
581
|
-
? html `<header class="widget-header"><h2 class="widget-title">${this._effectiveTitle}</h2></header>`
|
|
582
|
-
: ''}
|
|
737
|
+
${headerContent}
|
|
583
738
|
<div class="widget-body">
|
|
584
739
|
${bodyContent}
|
|
585
|
-
|
|
740
|
+
</div>
|
|
741
|
+
${showSpinnerOverlay
|
|
586
742
|
? html `<div class="loading-overlay" aria-busy="true" aria-live="polite">
|
|
587
|
-
|
|
588
|
-
|
|
743
|
+
<div class="loading-spinner" aria-hidden="true"></div>
|
|
744
|
+
</div>`
|
|
589
745
|
: nothing}
|
|
590
|
-
</div>
|
|
591
746
|
${filterToolbar}
|
|
592
747
|
</div>
|
|
593
748
|
`;
|
|
@@ -658,6 +813,9 @@ let TableWidget = class TableWidget extends LitElement {
|
|
|
658
813
|
__decorate([
|
|
659
814
|
property({ type: String })
|
|
660
815
|
], TableWidget.prototype, "title", void 0);
|
|
816
|
+
__decorate([
|
|
817
|
+
property({ type: Boolean, attribute: 'hide-refresh-button' })
|
|
818
|
+
], TableWidget.prototype, "hideRefreshButton", void 0);
|
|
661
819
|
__decorate([
|
|
662
820
|
property({ type: String, attribute: 'data-url' })
|
|
663
821
|
], TableWidget.prototype, "dataUrl", void 0);
|