@design.estate/dees-catalog 3.96.3 → 3.97.1

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.
@@ -0,0 +1,28 @@
1
+ <!--gitzone element-->
2
+ <!-- made by Task Venture Capital GmbH -->
3
+ <!-- checkout https://maintainedby.lossless.com for awesome OpenSource projects -->
4
+ <html lang="en">
5
+ <head>
6
+ <!--Lets set some basic meta tags-->
7
+ <meta
8
+ name="viewport"
9
+ content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width, height=device-height"
10
+ />
11
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
12
+
13
+ <!--Lets load standard fonts-->
14
+ <link rel="preconnect" href="https://assetbroker.lossless.one/" crossorigin>
15
+ <link rel="stylesheet" href="https://assetbroker.lossless.one/fonts/fonts.css">
16
+
17
+ <style>
18
+ body {
19
+ margin: 0px;
20
+ background: #222222;
21
+ }
22
+ </style>
23
+
24
+ <script type="module" src="/bundle.js"></script>
25
+ </head>
26
+ <body>
27
+ </body>
28
+ </html>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@design.estate/dees-catalog",
3
- "version": "3.96.3",
3
+ "version": "3.97.1",
4
4
  "private": false,
5
5
  "description": "A comprehensive library that provides dynamic web components for building sophisticated and modern web applications using JavaScript and TypeScript.",
6
6
  "main": "dist_ts_web/index.js",
@@ -35,11 +35,11 @@
35
35
  "devDependencies": {
36
36
  "@design.estate/dees-wcctools": "^3.9.2",
37
37
  "@git.zone/tsbuild": "^4.4.2",
38
- "@git.zone/tsbundle": "^2.11.1",
38
+ "@git.zone/tsbundle": "^2.11.0",
39
39
  "@git.zone/tstest": "^3.6.6",
40
40
  "@git.zone/tswatch": "^3.3.5",
41
41
  "@push.rocks/projectinfo": "^5.1.0",
42
- "@types/node": "^26.1.1"
42
+ "@types/node": "^26.1.0"
43
43
  },
44
44
  "files": [
45
45
  "ts/**/*",
@@ -3,6 +3,6 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@design.estate/dees-catalog',
6
- version: '3.96.3',
6
+ version: '3.97.1',
7
7
  description: 'A comprehensive library that provides dynamic web components for building sophisticated and modern web applications using JavaScript and TypeScript.'
8
8
  }
@@ -10,6 +10,7 @@ import * as domtools from '@design.estate/dees-domtools';
10
10
  import { demoFunc } from './demo.js';
11
11
  import { chartAreaStyles } from './styles.js';
12
12
  import { renderChartArea } from './template.js';
13
+ import { getEchartsSeriesColors, hexToRgba } from '../dees-chart-echarts-theme.js';
13
14
 
14
15
  import type { IChartApi, ISeriesApi, UTCTimestamp, MouseEventParams } from 'lightweight-charts';
15
16
  import { DeesServiceLibLoader, type ILightweightChartsBundle } from '../../../services/index.js';
@@ -17,9 +18,16 @@ import '../../00group-layout/dees-tile/dees-tile.js';
17
18
 
18
19
  export type ChartSeriesConfig = {
19
20
  name?: string;
21
+ /** Explicit series color (hex or hsl). Falls back to the shared catalog palette by index. */
22
+ color?: string;
20
23
  data: Array<{ x: any; y: number }>;
21
24
  }[];
22
25
 
26
+ export type TChartLegendStat = 'latest' | 'min' | 'max' | 'avg';
27
+ export type TChartPriceLine = 'avg' | 'max';
28
+
29
+ export const CHART_LEGEND_STATS: TChartLegendStat[] = ['latest', 'min', 'max', 'avg'];
30
+
23
31
  declare global {
24
32
  interface HTMLElementTagNameMap {
25
33
  'dees-chart-area': DeesChartArea;
@@ -68,6 +76,25 @@ export class DeesChartArea extends DeesElement {
68
76
  @property({ type: Number })
69
77
  accessor autoScrollInterval: number = 1000;
70
78
 
79
+ /**
80
+ * Which per-series stats appear in the footer legend, in the given order.
81
+ * Restrict (e.g. ['latest']) or empty ([] → color dot + name only) so the
82
+ * legend fits smaller tiles. Default shows all stats.
83
+ */
84
+ @property({ type: Array })
85
+ accessor legendStats: TChartLegendStat[] = [...CHART_LEGEND_STATS];
86
+
87
+ /** Hide the footer legend entirely. */
88
+ @property({ type: Boolean })
89
+ accessor hideLegend: boolean = false;
90
+
91
+ /**
92
+ * Which reference lines are drawn on the chart per series. The 'max' line
93
+ * keeps its existing rule of only appearing when max exceeds avg by >10%.
94
+ */
95
+ @property({ type: Array })
96
+ accessor chartLines: TChartPriceLine[] = ['avg', 'max'];
97
+
71
98
  private internalChartData: ChartSeriesConfig = [];
72
99
  private autoScrollTimer: number | null = null;
73
100
  private lcBundle: ILightweightChartsBundle | null = null;
@@ -75,21 +102,6 @@ export class DeesChartArea extends DeesElement {
75
102
  private priceLines: Map<string, any[]> = new Map();
76
103
  private tooltipEl: HTMLDivElement | null = null;
77
104
 
78
- private readonly CHART_COLORS = {
79
- dark: [
80
- 'hsl(217.2 91.2% 59.8%)',
81
- 'hsl(173.4 80.4% 40%)',
82
- 'hsl(280.3 87.4% 66.7%)',
83
- 'hsl(24.6 95% 53.1%)',
84
- ],
85
- light: [
86
- 'hsl(222.2 47.4% 51.2%)',
87
- 'hsl(142.1 76.2% 36.3%)',
88
- 'hsl(280.3 47.7% 50.2%)',
89
- 'hsl(20.5 90.2% 48.2%)',
90
- ]
91
- };
92
-
93
105
  constructor() {
94
106
  super();
95
107
  domtools.elementBasic.setup();
@@ -125,11 +137,18 @@ export class DeesChartArea extends DeesElement {
125
137
  .sort((a, b) => (a.time as number) - (b.time as number));
126
138
  }
127
139
 
128
- private hslToRgba(hslColor: string, alpha: number): string {
129
- const match = hslColor.match(/hsl\(([^)]+)\)/);
130
- if (!match) return hslColor;
140
+ private colorToRgba(color: string, alpha: number): string {
141
+ if (/^#[0-9a-fA-F]{6}$/.test(color)) {
142
+ return hexToRgba(color, alpha);
143
+ }
144
+ if (/^#[0-9a-fA-F]{3}$/.test(color)) {
145
+ const expanded = `#${color[1]}${color[1]}${color[2]}${color[2]}${color[3]}${color[3]}`;
146
+ return hexToRgba(expanded, alpha);
147
+ }
148
+ const match = color.match(/hsl\(([^)]+)\)/);
149
+ if (!match) return color;
131
150
  const parts = match[1].trim().split(/\s+/);
132
- if (parts.length < 3) return hslColor;
151
+ if (parts.length < 3) return color;
133
152
  const h = parseFloat(parts[0]) / 360;
134
153
  const s = parseFloat(parts[1]) / 100;
135
154
  const l = parseFloat(parts[2]) / 100;
@@ -155,13 +174,20 @@ export class DeesChartArea extends DeesElement {
155
174
  }
156
175
 
157
176
  private getSeriesColors(isDark: boolean) {
158
- return isDark ? this.CHART_COLORS.dark : this.CHART_COLORS.light;
177
+ // Shared catalog palette so the same index resolves to the same color
178
+ // in dees-chart-donut and every other echarts-based chart.
179
+ return getEchartsSeriesColors(!isDark);
180
+ }
181
+
182
+ private resolveSeriesColor(entry: { color?: string } | undefined, index: number, isDark: boolean): string {
183
+ if (entry?.color) return entry.color;
184
+ const palette = this.getSeriesColors(isDark);
185
+ return palette[index % palette.length];
159
186
  }
160
187
 
161
188
  private applyTheme() {
162
189
  if (!this.chart || !this.lcBundle) return;
163
190
  const isDark = !this.goBright;
164
- const colors = this.getSeriesColors(isDark);
165
191
 
166
192
  this.chart.applyOptions({
167
193
  layout: {
@@ -177,10 +203,10 @@ export class DeesChartArea extends DeesElement {
177
203
 
178
204
  let idx = 0;
179
205
  for (const [, api] of this.seriesApis) {
180
- const color = colors[idx % colors.length];
206
+ const color = this.resolveSeriesColor(this.chartSeries[idx], idx, isDark);
181
207
  api.applyOptions({
182
- topColor: this.hslToRgba(color, isDark ? 0.4 : 0.5),
183
- bottomColor: this.hslToRgba(color, 0),
208
+ topColor: this.colorToRgba(color, isDark ? 0.4 : 0.5),
209
+ bottomColor: this.colorToRgba(color, 0),
184
210
  lineColor: color,
185
211
  });
186
212
  idx++;
@@ -190,7 +216,6 @@ export class DeesChartArea extends DeesElement {
190
216
  private recreateSeries(chartSeries: ChartSeriesConfig) {
191
217
  if (!this.chart || !this.lcBundle) return;
192
218
  const isDark = !this.goBright;
193
- const colors = this.getSeriesColors(isDark);
194
219
 
195
220
  for (const [, api] of this.seriesApis) {
196
221
  this.chart.removeSeries(api);
@@ -199,10 +224,10 @@ export class DeesChartArea extends DeesElement {
199
224
  this.priceLines.clear();
200
225
 
201
226
  chartSeries.forEach((s, index) => {
202
- const color = colors[index % colors.length];
227
+ const color = this.resolveSeriesColor(s, index, isDark);
203
228
  const api = this.chart!.addSeries(this.lcBundle!.AreaSeries, {
204
- topColor: this.hslToRgba(color, isDark ? 0.4 : 0.5),
205
- bottomColor: this.hslToRgba(color, 0),
229
+ topColor: this.colorToRgba(color, isDark ? 0.4 : 0.5),
230
+ bottomColor: this.colorToRgba(color, 0),
206
231
  lineColor: color,
207
232
  lineWidth: 2,
208
233
  lineType: this.lcBundle!.LineType.Curved,
@@ -236,17 +261,17 @@ export class DeesChartArea extends DeesElement {
236
261
 
237
262
  private computeStats(chartSeries: ChartSeriesConfig) {
238
263
  const isDark = !this.goBright;
239
- const colors = this.getSeriesColors(isDark);
240
264
  this.seriesStats = chartSeries.map((s, index) => {
265
+ const color = this.resolveSeriesColor(s, index, isDark);
241
266
  const values = s.data.map(d => d.y);
242
267
  if (values.length === 0) {
243
- return { name: s.name || `series-${index}`, latest: 0, min: 0, max: 0, avg: 0, color: colors[index % colors.length] };
268
+ return { name: s.name || `series-${index}`, latest: 0, min: 0, max: 0, avg: 0, color };
244
269
  }
245
270
  const latest = values[values.length - 1];
246
271
  const min = Math.min(...values);
247
272
  const max = Math.max(...values);
248
273
  const avg = Math.round((values.reduce((sum, v) => sum + v, 0) / values.length) * 100) / 100;
249
- return { name: s.name || `series-${index}`, latest, min, max, avg, color: colors[index % colors.length] };
274
+ return { name: s.name || `series-${index}`, latest, min, max, avg, color };
250
275
  });
251
276
  }
252
277
 
@@ -270,20 +295,22 @@ export class DeesChartArea extends DeesElement {
270
295
 
271
296
  const lines: any[] = [];
272
297
 
273
- lines.push(api.createPriceLine({
274
- price: Math.round(avg * 100) / 100,
275
- color: this.hslToRgba(color, 0.5),
276
- lineWidth: 1,
277
- lineStyle: 2, // Dashed
278
- axisLabelVisible: true,
279
- title: 'avg',
280
- }));
298
+ if (this.chartLines.includes('avg')) {
299
+ lines.push(api.createPriceLine({
300
+ price: Math.round(avg * 100) / 100,
301
+ color: this.colorToRgba(color, 0.5),
302
+ lineWidth: 1,
303
+ lineStyle: 2, // Dashed
304
+ axisLabelVisible: true,
305
+ title: 'avg',
306
+ }));
307
+ }
281
308
 
282
309
  // Only show max line if it's more than 10% above average
283
- if (avg > 0 && (max - avg) / avg > 0.1) {
310
+ if (this.chartLines.includes('max') && avg > 0 && (max - avg) / avg > 0.1) {
284
311
  lines.push(api.createPriceLine({
285
312
  price: max,
286
- color: this.hslToRgba(color, 0.8),
313
+ color: this.colorToRgba(color, 0.8),
287
314
  lineWidth: 1,
288
315
  lineStyle: 1, // Dotted
289
316
  axisLabelVisible: true,
@@ -314,7 +341,6 @@ export class DeesChartArea extends DeesElement {
314
341
  const bgColor = isDark ? 'hsl(0 0% 9%)' : 'hsl(0 0% 100%)';
315
342
  const textColor = isDark ? 'hsl(0 0% 95%)' : 'hsl(0 0% 9%)';
316
343
  const borderColor = isDark ? 'hsl(0 0% 14.9%)' : 'hsl(0 0% 89.8%)';
317
- const colors = this.getSeriesColors(isDark);
318
344
 
319
345
  let html = '';
320
346
  let idx = 0;
@@ -323,7 +349,7 @@ export class DeesChartArea extends DeesElement {
323
349
  const data = param.seriesData.get(api);
324
350
  if (data && 'value' in data && (data as any).value !== undefined) {
325
351
  hasData = true;
326
- const color = colors[idx % colors.length];
352
+ const color = this.resolveSeriesColor(this.chartSeries[idx], idx, isDark);
327
353
  const formatted = this.yAxisFormatter((data as any).value);
328
354
  html += `<div style="display:flex;align-items:center;gap:8px;margin:${idx > 0 ? '6px' : '0'} 0;">
329
355
  <span style="display:inline-block;width:10px;height:10px;background:${color};border-radius:2px;"></span>
@@ -458,6 +484,10 @@ export class DeesChartArea extends DeesElement {
458
484
  }
459
485
  }
460
486
 
487
+ if (changedProperties.has('chartLines') && this.chart) {
488
+ this.refreshPriceLines();
489
+ }
490
+
461
491
  if ((changedProperties.has('yAxisScaling') || changedProperties.has('yAxisMax')) && this.chart) {
462
492
  for (const [, api] of this.seriesApis) {
463
493
  if (this.yAxisScaling === 'dynamic') {
@@ -486,7 +516,7 @@ export class DeesChartArea extends DeesElement {
486
516
  if (newSeries.length !== this.seriesApis.size) {
487
517
  this.recreateSeries(newSeries);
488
518
  } else {
489
- const colors = this.getSeriesColors(!this.goBright);
519
+ const isDark = !this.goBright;
490
520
  newSeries.forEach((s, index) => {
491
521
  const name = s.name || `series-${index}`;
492
522
  const api = this.seriesApis.get(name);
@@ -496,7 +526,13 @@ export class DeesChartArea extends DeesElement {
496
526
  return ms > cutoffTime;
497
527
  });
498
528
  api.setData(this.convertDataToLC(filtered));
499
- this.updatePriceLines(name, api, filtered, colors[index % colors.length]);
529
+ const color = this.resolveSeriesColor(s, index, isDark);
530
+ api.applyOptions({
531
+ topColor: this.colorToRgba(color, isDark ? 0.4 : 0.5),
532
+ bottomColor: this.colorToRgba(color, 0),
533
+ lineColor: color,
534
+ });
535
+ this.updatePriceLines(name, api, filtered, color);
500
536
  });
501
537
  this.computeStats(newSeries);
502
538
  }
@@ -528,13 +564,19 @@ export class DeesChartArea extends DeesElement {
528
564
  if (newSeries.length !== this.seriesApis.size) {
529
565
  this.recreateSeries(newSeries);
530
566
  } else {
531
- const colors = this.getSeriesColors(!this.goBright);
567
+ const isDark = !this.goBright;
532
568
  newSeries.forEach((s, index) => {
533
569
  const name = s.name || `series-${index}`;
534
570
  const api = this.seriesApis.get(name);
535
571
  if (!api) return;
536
572
  api.setData(this.convertDataToLC(s.data));
537
- this.updatePriceLines(name, api, s.data, colors[index % colors.length]);
573
+ const color = this.resolveSeriesColor(s, index, isDark);
574
+ api.applyOptions({
575
+ topColor: this.colorToRgba(color, isDark ? 0.4 : 0.5),
576
+ bottomColor: this.colorToRgba(color, 0),
577
+ lineColor: color,
578
+ });
579
+ this.updatePriceLines(name, api, s.data, color);
538
580
  });
539
581
  this.computeStats(newSeries);
540
582
  }
@@ -609,6 +651,16 @@ export class DeesChartArea extends DeesElement {
609
651
  });
610
652
  }
611
653
 
654
+ private refreshPriceLines() {
655
+ const isDark = !this.goBright;
656
+ this.chartSeries.forEach((s, index) => {
657
+ const name = s.name || `series-${index}`;
658
+ const api = this.seriesApis.get(name);
659
+ if (!api) return;
660
+ this.updatePriceLines(name, api, s.data, this.resolveSeriesColor(s, index, isDark));
661
+ });
662
+ }
663
+
612
664
  private startAutoScroll() {
613
665
  if (this.autoScrollTimer) return;
614
666
  this.autoScrollTimer = window.setInterval(() => {
@@ -450,7 +450,22 @@ export const demoFunc = () => {
450
450
  .yAxisFormatter=${(val: number) => `${val}`}
451
451
  ></dees-chart-area>
452
452
  </div>
453
-
453
+
454
+ <div class="chart-container" style="margin-top: 20px; min-height: 260px; height: 260px;">
455
+ <dees-chart-area
456
+ id="compact-chart"
457
+ style="height: 260px;"
458
+ .label=${'Compact tile (legendStats=[avg], chartLines=[], explicit colors)'}
459
+ .legendStats=${['avg']}
460
+ .chartLines=${[]}
461
+ .series=${initialDatasets.system.series.map((s, idx) => ({
462
+ ...s,
463
+ color: idx === 0 ? '#0a84ff' : '#34c759',
464
+ }))}
465
+ .yAxisFormatter=${initialFormatters.system}
466
+ ></dees-chart-area>
467
+ </div>
468
+
454
469
  <div class="info">
455
470
  Real-time monitoring with 2-minute rolling window •
456
471
  Updates every second with smooth value transitions •
@@ -51,11 +51,12 @@ export const chartAreaStyles = [
51
51
  inset: 0 0 4px 0;
52
52
  }
53
53
  .statsBar {
54
- height: 32px;
55
- padding: 0 16px;
54
+ min-height: 32px;
55
+ padding: 4px 16px;
56
56
  display: flex;
57
57
  align-items: center;
58
- gap: 24px;
58
+ flex-wrap: wrap;
59
+ gap: 4px 24px;
59
60
  width: 100%;
60
61
  box-sizing: border-box;
61
62
  }
@@ -12,16 +12,17 @@ export const renderChartArea = (component: DeesChartArea): TemplateResult => {
12
12
  </button>
13
13
  </div>
14
14
  <div class="chartContainer"></div>
15
- ${component.seriesStats.length > 0 ? html`
15
+ ${component.seriesStats.length > 0 && !component.hideLegend ? html`
16
16
  <div slot="footer" class="statsBar">
17
17
  ${component.seriesStats.map(s => html`
18
18
  <div class="statsSeries">
19
19
  <span class="statsColor" style="background:${s.color}"></span>
20
20
  <span class="statsName">${s.name}</span>
21
- <span class="statsItem">latest <strong>${component.yAxisFormatter(s.latest)}</strong></span>
22
- <span class="statsItem">min <strong>${component.yAxisFormatter(s.min)}</strong></span>
23
- <span class="statsItem">max <strong>${component.yAxisFormatter(s.max)}</strong></span>
24
- <span class="statsItem">avg <strong>${component.yAxisFormatter(s.avg)}</strong></span>
21
+ ${component.legendStats
22
+ .filter(key => key === 'latest' || key === 'min' || key === 'max' || key === 'avg')
23
+ .map(key => html`
24
+ <span class="statsItem">${key} <strong>${component.yAxisFormatter(s[key])}</strong></span>
25
+ `)}
25
26
  </div>
26
27
  `)}
27
28
  </div>