@design.estate/dees-catalog 3.98.0 → 3.99.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -160989,6 +160989,16 @@ var chartAreaStyles = [
160989
160989
  position: absolute;
160990
160990
  inset: 0 0 4px 0;
160991
160991
  }
160992
+ .rangeSelectionOverlay {
160993
+ position: absolute;
160994
+ display: none;
160995
+ top: 0;
160996
+ bottom: 0;
160997
+ z-index: 3;
160998
+ pointer-events: none;
160999
+ background: color-mix(in srgb, var(--dees-color-accent, #3b82f6) 18%, transparent);
161000
+ border-inline: 1px solid color-mix(in srgb, var(--dees-color-accent, #3b82f6) 70%, transparent);
161001
+ }
160992
161002
  .statsBar {
160993
161003
  min-height: 32px;
160994
161004
  padding: 4px 16px;
@@ -161054,7 +161064,9 @@ var renderChartArea = /* @__PURE__ */ __name((component) => {
161054
161064
  <dees-icon .icon=${component.isFullPage ? "lucide:Minimize2" : "lucide:Maximize2"} .iconSize=${14}></dees-icon>
161055
161065
  </button>
161056
161066
  </div>
161057
- <div class="chartContainer"></div>
161067
+ <div class="chartContainer">
161068
+ <div class="rangeSelectionOverlay" aria-hidden="true"></div>
161069
+ </div>
161058
161070
  ${component.seriesStats.length > 0 && !component.hideLegend ? b2`
161059
161071
  <div slot="footer" class="statsBar">
161060
161072
  ${component.seriesStats.map((s9) => b2`
@@ -161072,6 +161084,72 @@ var renderChartArea = /* @__PURE__ */ __name((component) => {
161072
161084
  `;
161073
161085
  }, "renderChartArea");
161074
161086
 
161087
+ // ts_web/elements/00group-chart/dees-chart-area/realtime-updates.ts
161088
+ var planRealtimeChartUpdates = /* @__PURE__ */ __name((renderedData, canonicalData) => {
161089
+ if (canonicalData.length === 0) {
161090
+ return {
161091
+ replacementRequired: renderedData.length !== 0,
161092
+ updates: [],
161093
+ pruneRecommended: false,
161094
+ pruneRequired: false
161095
+ };
161096
+ }
161097
+ if (renderedData.length === 0) {
161098
+ return {
161099
+ replacementRequired: false,
161100
+ updates: canonicalData.map((point4) => ({ point: point4, historicalUpdate: false })),
161101
+ pruneRecommended: false,
161102
+ pruneRequired: false
161103
+ };
161104
+ }
161105
+ const renderedByTime = new Map(renderedData.map((point4) => [point4.time, point4]));
161106
+ const canonicalByTime = new Map(canonicalData.map((point4) => [point4.time, point4]));
161107
+ const canonicalStart = canonicalData[0].time;
161108
+ const renderedLastTime = renderedData[renderedData.length - 1].time;
161109
+ const hasStructuralDeletion = renderedData.some(
161110
+ (point4) => point4.time >= canonicalStart && !canonicalByTime.has(point4.time)
161111
+ );
161112
+ if (hasStructuralDeletion) {
161113
+ return {
161114
+ replacementRequired: true,
161115
+ updates: [],
161116
+ pruneRecommended: false,
161117
+ pruneRequired: false
161118
+ };
161119
+ }
161120
+ const updates = [];
161121
+ for (const canonicalPoint of canonicalData) {
161122
+ const renderedPoint = renderedByTime.get(canonicalPoint.time);
161123
+ if (!renderedPoint) {
161124
+ if (canonicalPoint.time < renderedLastTime) {
161125
+ return {
161126
+ replacementRequired: true,
161127
+ updates: [],
161128
+ pruneRecommended: false,
161129
+ pruneRequired: false
161130
+ };
161131
+ }
161132
+ updates.push({ point: canonicalPoint, historicalUpdate: false });
161133
+ continue;
161134
+ }
161135
+ if (renderedPoint.value !== canonicalPoint.value) {
161136
+ updates.push({
161137
+ point: canonicalPoint,
161138
+ historicalUpdate: canonicalPoint.time < renderedLastTime
161139
+ });
161140
+ }
161141
+ }
161142
+ const firstCanonicalPointIndex = renderedData.findIndex((point4) => point4.time >= canonicalStart);
161143
+ const retainedPrefixLength = firstCanonicalPointIndex === -1 ? renderedData.length : firstCanonicalPointIndex;
161144
+ const projectedLength = renderedData.length + updates.filter((update) => !renderedByTime.has(update.point.time)).length;
161145
+ const retentionLimit = Math.max(canonicalData.length * 2, 2);
161146
+ const pruneRecommended = retainedPrefixLength > 0 && projectedLength > retentionLimit;
161147
+ const hardRetentionLimit = Math.max(canonicalData.length * 3, 3);
161148
+ const pruneRequired = retainedPrefixLength > 0 && projectedLength > hardRetentionLimit;
161149
+ return { replacementRequired: false, updates, pruneRecommended, pruneRequired };
161150
+ }, "planRealtimeChartUpdates");
161151
+ var haveSameSeriesLayout = /* @__PURE__ */ __name((renderedNames, canonicalNames) => renderedNames.length === canonicalNames.length && renderedNames.every((name, index3) => name === canonicalNames[index3]), "haveSameSeriesLayout");
161152
+
161075
161153
  // ts_web/elements/00group-chart/dees-chart-echarts-theme.ts
161076
161154
  init_theme();
161077
161155
  init_fonts();
@@ -161157,10 +161235,10 @@ init_dist_ts25();
161157
161235
  init_dist_ts24();
161158
161236
  init_services();
161159
161237
  init_dees_tile();
161160
- var _chartLines_dec, _hideLegend_dec, _legendStats_dec, _autoScrollInterval_dec, _yAxisMax_dec, _yAxisScaling_dec, _realtimeMode_dec, _rollingWindow_dec, _yAxisFormatter_dec, _series_dec, _label_dec5, _isFullPage_dec, _seriesStats_dec, _chart_dec, _a56, _DeesChartArea_decorators, _init57, _chart, _seriesStats, _isFullPage, _label5, _series, _yAxisFormatter, _rollingWindow, _realtimeMode, _yAxisScaling, _yAxisMax, _autoScrollInterval, _legendStats, _hideLegend, _chartLines;
161238
+ var _selectedRange_dec, _rangeSelectionEnabled_dec, _chartLines_dec, _hideLegend_dec, _legendStats_dec, _autoScrollInterval_dec, _yAxisMax_dec, _yAxisScaling_dec, _realtimeMode_dec, _rollingWindow_dec, _yAxisFormatter_dec, _series_dec, _label_dec5, _isFullPage_dec, _seriesStats_dec, _chart_dec, _a56, _DeesChartArea_decorators, _init57, _chart, _seriesStats, _isFullPage, _label5, _series, _yAxisFormatter, _rollingWindow, _realtimeMode, _yAxisScaling, _yAxisMax, _autoScrollInterval, _legendStats, _hideLegend, _chartLines, _rangeSelectionEnabled, _selectedRange;
161161
161239
  var CHART_LEGEND_STATS = ["latest", "min", "max", "avg"];
161162
161240
  _DeesChartArea_decorators = [customElement("dees-chart-area")];
161163
- var _DeesChartArea = class _DeesChartArea extends (_a56 = DeesElement, _chart_dec = [r5()], _seriesStats_dec = [r5()], _isFullPage_dec = [r5()], _label_dec5 = [n5()], _series_dec = [n5({ type: Array })], _yAxisFormatter_dec = [n5({ attribute: false })], _rollingWindow_dec = [n5({ type: Number })], _realtimeMode_dec = [n5({ type: Boolean })], _yAxisScaling_dec = [n5({ type: String })], _yAxisMax_dec = [n5({ type: Number })], _autoScrollInterval_dec = [n5({ type: Number })], _legendStats_dec = [n5({ type: Array })], _hideLegend_dec = [n5({ type: Boolean })], _chartLines_dec = [n5({ type: Array })], _a56) {
161241
+ var _DeesChartArea = class _DeesChartArea extends (_a56 = DeesElement, _chart_dec = [r5()], _seriesStats_dec = [r5()], _isFullPage_dec = [r5()], _label_dec5 = [n5()], _series_dec = [n5({ type: Array })], _yAxisFormatter_dec = [n5({ attribute: false })], _rollingWindow_dec = [n5({ type: Number })], _realtimeMode_dec = [n5({ type: Boolean })], _yAxisScaling_dec = [n5({ type: String })], _yAxisMax_dec = [n5({ type: Number })], _autoScrollInterval_dec = [n5({ type: Number })], _legendStats_dec = [n5({ type: Array })], _hideLegend_dec = [n5({ type: Boolean })], _chartLines_dec = [n5({ type: Array })], _rangeSelectionEnabled_dec = [n5({ type: Boolean })], _selectedRange_dec = [n5({ attribute: false })], _a56) {
161164
161242
  constructor() {
161165
161243
  super();
161166
161244
  __privateAdd(this, _chart, __runInitializers(_init57, 8, this, null)), __runInitializers(_init57, 11, this);
@@ -161177,25 +161255,112 @@ var _DeesChartArea = class _DeesChartArea extends (_a56 = DeesElement, _chart_de
161177
161255
  __privateAdd(this, _legendStats, __runInitializers(_init57, 52, this, [...CHART_LEGEND_STATS])), __runInitializers(_init57, 55, this);
161178
161256
  __privateAdd(this, _hideLegend, __runInitializers(_init57, 56, this, false)), __runInitializers(_init57, 59, this);
161179
161257
  __privateAdd(this, _chartLines, __runInitializers(_init57, 60, this, ["avg", "max"])), __runInitializers(_init57, 63, this);
161258
+ __privateAdd(this, _rangeSelectionEnabled, __runInitializers(_init57, 64, this, false)), __runInitializers(_init57, 67, this);
161259
+ __privateAdd(this, _selectedRange, __runInitializers(_init57, 68, this, null)), __runInitializers(_init57, 71, this);
161180
161260
  __publicField(this, "internalChartData", []);
161181
161261
  __publicField(this, "autoScrollTimer", null);
161182
161262
  __publicField(this, "lcBundle", null);
161183
161263
  __publicField(this, "seriesApis", /* @__PURE__ */ new Map());
161184
161264
  __publicField(this, "priceLines", /* @__PURE__ */ new Map());
161185
161265
  __publicField(this, "tooltipEl", null);
161266
+ __publicField(this, "crosshairActive", false);
161267
+ __publicField(this, "pendingRealtimePrune", false);
161268
+ __publicField(this, "crosshairMoveHandler", null);
161269
+ __publicField(this, "chartContainer", null);
161270
+ __publicField(this, "rangeOverlay", null);
161271
+ __publicField(this, "rangeResizeObserver", null);
161272
+ __publicField(this, "rangeDrag", null);
161273
+ __publicField(this, "rangePointerDownHandler", /* @__PURE__ */ __name((event) => {
161274
+ if (!this.rangeSelectionEnabled || event.button !== 0 || !this.chartContainer) return;
161275
+ const x3 = this.getRangePointerX(event);
161276
+ const timeMs = this.coordinateToEpochMs(x3);
161277
+ if (timeMs === null) return;
161278
+ event.preventDefault();
161279
+ this.rangeDrag = { pointerId: event.pointerId, startX: x3, startMs: timeMs };
161280
+ try {
161281
+ this.chartContainer.setPointerCapture(event.pointerId);
161282
+ } catch {
161283
+ }
161284
+ window.addEventListener("keydown", this.rangeKeydownHandler);
161285
+ this.paintRangeOverlay(x3, x3);
161286
+ }, "rangePointerDownHandler"));
161287
+ __publicField(this, "rangePointerMoveHandler", /* @__PURE__ */ __name((event) => {
161288
+ if (!this.rangeDrag || event.pointerId !== this.rangeDrag.pointerId) return;
161289
+ event.preventDefault();
161290
+ this.paintRangeOverlay(this.rangeDrag.startX, this.getRangePointerX(event));
161291
+ }, "rangePointerMoveHandler"));
161292
+ __publicField(this, "rangePointerUpHandler", /* @__PURE__ */ __name((event) => {
161293
+ const drag = this.rangeDrag;
161294
+ if (!drag || event.pointerId !== drag.pointerId) return;
161295
+ const endX = this.getRangePointerX(event);
161296
+ const endMs = this.coordinateToEpochMs(endX);
161297
+ this.finishRangeDrag(event.pointerId);
161298
+ if (endMs === null || Math.abs(endX - drag.startX) < 4) {
161299
+ this.syncRangeOverlay();
161300
+ return;
161301
+ }
161302
+ const from3 = Math.min(drag.startMs, endMs);
161303
+ const to = Math.max(drag.startMs, endMs);
161304
+ if (!Number.isSafeInteger(from3) || !Number.isSafeInteger(to) || from3 >= to) {
161305
+ this.syncRangeOverlay();
161306
+ return;
161307
+ }
161308
+ this.dispatchEvent(new CustomEvent("range-change", {
161309
+ detail: { from: from3, to },
161310
+ bubbles: true,
161311
+ composed: true
161312
+ }));
161313
+ this.syncRangeOverlay();
161314
+ }, "rangePointerUpHandler"));
161315
+ __publicField(this, "rangePointerCancelHandler", /* @__PURE__ */ __name((event) => {
161316
+ if (!this.rangeDrag || event.pointerId !== this.rangeDrag.pointerId) return;
161317
+ this.finishRangeDrag(event.pointerId);
161318
+ this.syncRangeOverlay();
161319
+ }, "rangePointerCancelHandler"));
161320
+ __publicField(this, "rangeKeydownHandler", /* @__PURE__ */ __name((event) => {
161321
+ if (event.key !== "Escape" || !this.rangeDrag) return;
161322
+ const pointerId = this.rangeDrag.pointerId;
161323
+ this.finishRangeDrag(pointerId);
161324
+ this.syncRangeOverlay();
161325
+ }, "rangeKeydownHandler"));
161326
+ __publicField(this, "rangeVisibleTimeChangeHandler", /* @__PURE__ */ __name(() => {
161327
+ this.syncRangeOverlay();
161328
+ }, "rangeVisibleTimeChangeHandler"));
161329
+ __publicField(this, "chartMouseLeaveHandler", /* @__PURE__ */ __name(() => {
161330
+ this.chart?.clearCrosshairPosition();
161331
+ this.handleCrosshairCleared();
161332
+ }, "chartMouseLeaveHandler"));
161186
161333
  domtools_elementbasic_exports.setup();
161187
161334
  this.registerGarbageFunction(async () => {
161188
161335
  this.stopAutoScroll();
161189
- if (this.chart) {
161336
+ const chart = this.chart;
161337
+ const crosshairMoveHandler = this.crosshairMoveHandler;
161338
+ this.crosshairMoveHandler = null;
161339
+ this.chartContainer?.removeEventListener("mouseleave", this.chartMouseLeaveHandler);
161340
+ this.teardownRangeSelection();
161341
+ this.chart = null;
161342
+ this.rangeResizeObserver?.disconnect();
161343
+ this.rangeResizeObserver = null;
161344
+ this.chartContainer = null;
161345
+ this.tooltipEl = null;
161346
+ this.rangeOverlay = null;
161347
+ this.crosshairActive = false;
161348
+ this.pendingRealtimePrune = false;
161349
+ if (crosshairMoveHandler && chart) {
161190
161350
  try {
161191
- this.chart.remove();
161192
- this.chart = null;
161193
- this.seriesApis.clear();
161194
- this.priceLines.clear();
161195
- } catch (e10) {
161196
- console.error("Error destroying chart:", e10);
161351
+ chart.unsubscribeCrosshairMove(crosshairMoveHandler);
161352
+ } catch (error) {
161353
+ console.error("Error unsubscribing chart crosshair:", error);
161197
161354
  }
161198
161355
  }
161356
+ try {
161357
+ chart?.remove();
161358
+ } catch (error) {
161359
+ console.error("Error destroying chart:", error);
161360
+ } finally {
161361
+ this.seriesApis.clear();
161362
+ this.priceLines.clear();
161363
+ }
161199
161364
  });
161200
161365
  }
161201
161366
  get chartSeries() {
@@ -161205,11 +161370,116 @@ var _DeesChartArea = class _DeesChartArea extends (_a56 = DeesElement, _chart_de
161205
161370
  return renderChartArea(this);
161206
161371
  }
161207
161372
  // --- Helpers ---
161373
+ getRangePointerX(event) {
161374
+ const rect = this.chartContainer.getBoundingClientRect();
161375
+ return Math.min(Math.max(event.clientX - rect.left, 0), rect.width);
161376
+ }
161377
+ coordinateToEpochMs(coordinateArg) {
161378
+ const time = this.chart?.timeScale().coordinateToTime(coordinateArg);
161379
+ if (typeof time !== "number" || !Number.isFinite(time)) return null;
161380
+ const epochMs = Math.round(time * 1e3);
161381
+ return Number.isSafeInteger(epochMs) && epochMs >= 0 ? epochMs : null;
161382
+ }
161383
+ paintRangeOverlay(firstXArg, secondXArg) {
161384
+ if (!this.rangeOverlay) return;
161385
+ const left = Math.min(firstXArg, secondXArg);
161386
+ const width = Math.abs(secondXArg - firstXArg);
161387
+ this.rangeOverlay.style.display = "block";
161388
+ this.rangeOverlay.style.left = `${left}px`;
161389
+ this.rangeOverlay.style.width = `${Math.max(width, 1)}px`;
161390
+ }
161391
+ finishRangeDrag(pointerIdArg) {
161392
+ this.rangeDrag = null;
161393
+ window.removeEventListener("keydown", this.rangeKeydownHandler);
161394
+ try {
161395
+ this.chartContainer?.releasePointerCapture(pointerIdArg);
161396
+ } catch {
161397
+ }
161398
+ }
161399
+ setupRangeSelection() {
161400
+ if (!this.chartContainer) return;
161401
+ this.rangeOverlay = this.chartContainer.querySelector(".rangeSelectionOverlay");
161402
+ this.chart?.timeScale().subscribeVisibleTimeRangeChange(this.rangeVisibleTimeChangeHandler);
161403
+ this.chartContainer.addEventListener("pointerdown", this.rangePointerDownHandler);
161404
+ this.chartContainer.addEventListener("pointermove", this.rangePointerMoveHandler);
161405
+ this.chartContainer.addEventListener("pointerup", this.rangePointerUpHandler);
161406
+ this.chartContainer.addEventListener("pointercancel", this.rangePointerCancelHandler);
161407
+ this.chartContainer.addEventListener("lostpointercapture", this.rangePointerCancelHandler);
161408
+ this.rangeResizeObserver = new ResizeObserver(() => this.syncRangeOverlay());
161409
+ this.rangeResizeObserver.observe(this.chartContainer);
161410
+ this.syncRangeOverlay();
161411
+ }
161412
+ teardownRangeSelection() {
161413
+ if (!this.chartContainer) return;
161414
+ this.chart?.timeScale().unsubscribeVisibleTimeRangeChange(this.rangeVisibleTimeChangeHandler);
161415
+ this.chartContainer.removeEventListener("pointerdown", this.rangePointerDownHandler);
161416
+ this.chartContainer.removeEventListener("pointermove", this.rangePointerMoveHandler);
161417
+ this.chartContainer.removeEventListener("pointerup", this.rangePointerUpHandler);
161418
+ this.chartContainer.removeEventListener("pointercancel", this.rangePointerCancelHandler);
161419
+ this.chartContainer.removeEventListener("lostpointercapture", this.rangePointerCancelHandler);
161420
+ window.removeEventListener("keydown", this.rangeKeydownHandler);
161421
+ if (this.rangeDrag) {
161422
+ try {
161423
+ this.chartContainer.releasePointerCapture(this.rangeDrag.pointerId);
161424
+ } catch {
161425
+ }
161426
+ }
161427
+ this.rangeDrag = null;
161428
+ }
161429
+ syncRangeOverlay() {
161430
+ if (!this.rangeOverlay || !this.chart || !this.rangeSelectionEnabled || !this.selectedRange) {
161431
+ if (this.rangeOverlay) this.rangeOverlay.style.display = "none";
161432
+ return;
161433
+ }
161434
+ const { from: from3, to } = this.selectedRange;
161435
+ if (!Number.isSafeInteger(from3) || !Number.isSafeInteger(to) || from3 < 0 || to <= from3) {
161436
+ this.rangeOverlay.style.display = "none";
161437
+ return;
161438
+ }
161439
+ const fromCoordinate = this.chart.timeScale().timeToCoordinate(
161440
+ Math.floor(from3 / 1e3)
161441
+ );
161442
+ const toCoordinate = this.chart.timeScale().timeToCoordinate(
161443
+ Math.floor(to / 1e3)
161444
+ );
161445
+ if (fromCoordinate === null || toCoordinate === null) {
161446
+ this.rangeOverlay.style.display = "none";
161447
+ return;
161448
+ }
161449
+ this.paintRangeOverlay(fromCoordinate, toCoordinate);
161450
+ }
161208
161451
  convertDataToLC(data) {
161209
- return data.map((point4) => {
161452
+ const pointsByTime = /* @__PURE__ */ new Map();
161453
+ for (const point4 of data) {
161210
161454
  const ms = typeof point4.x === "number" ? point4.x : new Date(point4.x).getTime();
161211
- return { time: Math.floor(ms / 1e3), value: point4.y };
161212
- }).sort((a4, b4) => a4.time - b4.time);
161455
+ const time = Math.floor(ms / 1e3);
161456
+ if (!Number.isFinite(time) || !Number.isFinite(point4.y)) continue;
161457
+ pointsByTime.set(time, { time, value: point4.y });
161458
+ }
161459
+ return [...pointsByTime.values()].sort((a4, b4) => a4.time - b4.time);
161460
+ }
161461
+ getSeriesName(series, index3) {
161462
+ return series.name || `series-${index3}`;
161463
+ }
161464
+ getSeriesKey(series, index3) {
161465
+ return `${index3}:${this.getSeriesName(series, index3)}`;
161466
+ }
161467
+ getCanonicalChartSeries(chartSeries) {
161468
+ const cutoffTime = Date.now() - this.rollingWindow;
161469
+ return chartSeries.map((series) => ({
161470
+ ...series,
161471
+ data: this.convertDataToLC(series.data.filter((point4) => {
161472
+ if (!this.realtimeMode || this.rollingWindow <= 0) return true;
161473
+ const ms = typeof point4.x === "number" ? point4.x : new Date(point4.x).getTime();
161474
+ return ms > cutoffTime;
161475
+ })).map((point4) => ({
161476
+ x: point4.time * 1e3,
161477
+ y: point4.value
161478
+ }))
161479
+ }));
161480
+ }
161481
+ getRenderedSeriesData(api) {
161482
+ return api.data().filter((point4) => "value" in point4).map((point4) => ({ time: point4.time, value: point4.value }));
161213
161483
  }
161214
161484
  colorToRgba(color2, alpha2) {
161215
161485
  if (/^#[0-9a-fA-F]{6}$/.test(color2)) {
@@ -161301,7 +161571,7 @@ var _DeesChartArea = class _DeesChartArea extends (_a56 = DeesElement, _chart_de
161301
161571
  crosshairMarkerRadius: 4
161302
161572
  });
161303
161573
  api.setData(this.convertDataToLC(s9.data));
161304
- this.updatePriceLines(s9.name || `series-${index3}`, api, s9.data, color2);
161574
+ this.updatePriceLines(this.getSeriesKey(s9, index3), api, s9.data, color2);
161305
161575
  if (this.yAxisScaling !== "dynamic") {
161306
161576
  api.applyOptions({
161307
161577
  autoscaleInfoProvider: /* @__PURE__ */ __name(() => ({
@@ -161309,13 +161579,63 @@ var _DeesChartArea = class _DeesChartArea extends (_a56 = DeesElement, _chart_de
161309
161579
  }), "autoscaleInfoProvider")
161310
161580
  });
161311
161581
  }
161312
- this.seriesApis.set(s9.name || `series-${index3}`, api);
161582
+ this.seriesApis.set(this.getSeriesKey(s9, index3), api);
161313
161583
  });
161314
161584
  this.computeStats(chartSeries);
161315
- if (this.rollingWindow <= 0) {
161585
+ this.pendingRealtimePrune = false;
161586
+ if (this.realtimeMode && this.rollingWindow > 0) {
161587
+ void this.updateTimeWindow();
161588
+ } else {
161316
161589
  this.chart.timeScale().fitContent();
161317
161590
  }
161318
161591
  }
161592
+ updateSeriesPresentation(chartSeries) {
161593
+ const isDark = !this.goBright;
161594
+ chartSeries.forEach((series, index3) => {
161595
+ const seriesKey = this.getSeriesKey(series, index3);
161596
+ const api = this.seriesApis.get(seriesKey);
161597
+ if (!api) return;
161598
+ const color2 = this.resolveSeriesColor(series, index3, isDark);
161599
+ api.applyOptions({
161600
+ topColor: this.colorToRgba(color2, isDark ? 0.4 : 0.5),
161601
+ bottomColor: this.colorToRgba(color2, 0),
161602
+ lineColor: color2
161603
+ });
161604
+ this.updatePriceLines(seriesKey, api, series.data, color2);
161605
+ });
161606
+ this.computeStats(chartSeries);
161607
+ if (this.yAxisScaling === "dynamic") {
161608
+ const allValues = chartSeries.flatMap((series) => series.data.map((point4) => point4.y));
161609
+ if (allValues.length > 0) {
161610
+ const dynamicMax = Math.ceil(Math.max(...allValues) * 1.1);
161611
+ for (const [, api] of this.seriesApis) {
161612
+ api.applyOptions({
161613
+ autoscaleInfoProvider: /* @__PURE__ */ __name(() => ({ priceRange: { minValue: 0, maxValue: dynamicMax } }), "autoscaleInfoProvider")
161614
+ });
161615
+ }
161616
+ }
161617
+ }
161618
+ }
161619
+ handleCrosshairCleared() {
161620
+ this.crosshairActive = false;
161621
+ if (this.tooltipEl) this.tooltipEl.style.display = "none";
161622
+ this.flushPendingRealtimePrune();
161623
+ }
161624
+ clearCrosshairForDataReplacement() {
161625
+ this.pendingRealtimePrune = false;
161626
+ this.chart?.clearCrosshairPosition();
161627
+ this.crosshairActive = false;
161628
+ if (this.tooltipEl) this.tooltipEl.style.display = "none";
161629
+ }
161630
+ flushPendingRealtimePrune() {
161631
+ if (!this.pendingRealtimePrune || this.crosshairActive || !this.chart || !this.realtimeMode || this.rollingWindow <= 0) return;
161632
+ const canonicalSeries = this.getCanonicalChartSeries(this.internalChartData);
161633
+ canonicalSeries.forEach((series, index3) => {
161634
+ const api = this.seriesApis.get(this.getSeriesKey(series, index3));
161635
+ if (api) api.setData(this.convertDataToLC(series.data));
161636
+ });
161637
+ this.pendingRealtimePrune = false;
161638
+ }
161319
161639
  computeStats(chartSeries) {
161320
161640
  const isDark = !this.goBright;
161321
161641
  this.seriesStats = chartSeries.map((s9, index3) => {
@@ -161375,13 +161695,16 @@ var _DeesChartArea = class _DeesChartArea extends (_a56 = DeesElement, _chart_de
161375
161695
  this.tooltipEl = document.createElement("div");
161376
161696
  this.tooltipEl.className = "lw-tooltip";
161377
161697
  this.tooltipEl.style.display = "none";
161378
- this.shadowRoot.querySelector(".chartContainer")?.appendChild(this.tooltipEl);
161379
- this.chart.subscribeCrosshairMove((param) => {
161698
+ this.chartContainer = this.shadowRoot.querySelector(".chartContainer");
161699
+ this.chartContainer?.appendChild(this.tooltipEl);
161700
+ this.chartContainer?.addEventListener("mouseleave", this.chartMouseLeaveHandler);
161701
+ this.crosshairMoveHandler = (param) => {
161380
161702
  if (!this.tooltipEl) return;
161381
- if (!param.point || !param.time || param.point.x < 0 || param.point.y < 0) {
161382
- this.tooltipEl.style.display = "none";
161703
+ if (!param.point || param.time === void 0 || param.point.x < 0 || param.point.y < 0) {
161704
+ this.handleCrosshairCleared();
161383
161705
  return;
161384
161706
  }
161707
+ this.crosshairActive = true;
161385
161708
  const isDark = !this.goBright;
161386
161709
  const bgColor = isDark ? "hsl(0 0% 9%)" : "hsl(0 0% 100%)";
161387
161710
  const textColor = isDark ? "hsl(0 0% 95%)" : "hsl(0 0% 9%)";
@@ -161389,11 +161712,13 @@ var _DeesChartArea = class _DeesChartArea extends (_a56 = DeesElement, _chart_de
161389
161712
  let html8 = "";
161390
161713
  let idx = 0;
161391
161714
  let hasData = false;
161392
- for (const [name, api] of this.seriesApis) {
161715
+ for (const api of this.seriesApis.values()) {
161393
161716
  const data = param.seriesData.get(api);
161394
161717
  if (data && "value" in data && data.value !== void 0) {
161395
161718
  hasData = true;
161396
- const color2 = this.resolveSeriesColor(this.chartSeries[idx], idx, isDark);
161719
+ const series = this.internalChartData[idx];
161720
+ const name = series ? this.getSeriesName(series, idx) : `series-${idx}`;
161721
+ const color2 = this.resolveSeriesColor(series, idx, isDark);
161397
161722
  const formatted = this.yAxisFormatter(data.value);
161398
161723
  html8 += `<div style="display:flex;align-items:center;gap:8px;margin:${idx > 0 ? "6px" : "0"} 0;">
161399
161724
  <span style="display:inline-block;width:10px;height:10px;background:${color2};border-radius:2px;"></span>
@@ -161419,13 +161744,17 @@ var _DeesChartArea = class _DeesChartArea extends (_a56 = DeesElement, _chart_de
161419
161744
  if (left + 200 > containerWidth) left = param.point.x - 216;
161420
161745
  this.tooltipEl.style.left = `${left}px`;
161421
161746
  this.tooltipEl.style.top = `${param.point.y - 16}px`;
161422
- });
161747
+ };
161748
+ this.chart.subscribeCrosshairMove(this.crosshairMoveHandler);
161423
161749
  }
161424
161750
  // --- Lifecycle ---
161425
161751
  async firstUpdated() {
161426
161752
  await this.domtoolsPromise;
161753
+ if (!this.isConnected) return;
161427
161754
  this.lcBundle = await DeesServiceLibLoader.getInstance().loadLightweightCharts();
161755
+ if (!this.isConnected) return;
161428
161756
  await new Promise((resolve2) => requestAnimationFrame(resolve2));
161757
+ if (!this.isConnected) return;
161429
161758
  const chartContainer = this.shadowRoot.querySelector(".chartContainer");
161430
161759
  if (!chartContainer) return;
161431
161760
  const isDark = !this.goBright;
@@ -161480,9 +161809,12 @@ var _DeesChartArea = class _DeesChartArea extends (_a56 = DeesElement, _chart_de
161480
161809
  ]
161481
161810
  }
161482
161811
  ];
161483
- this.internalChartData = chartSeries;
161484
- this.recreateSeries(chartSeries);
161812
+ const canonicalSeries = this.getCanonicalChartSeries(chartSeries);
161813
+ this.internalChartData = canonicalSeries;
161814
+ this.recreateSeries(canonicalSeries);
161485
161815
  this.setupTooltip();
161816
+ this.setupRangeSelection();
161817
+ this.syncAutoScroll();
161486
161818
  } catch (error) {
161487
161819
  console.error("Failed to initialize chart:", error);
161488
161820
  }
@@ -161492,23 +161824,29 @@ var _DeesChartArea = class _DeesChartArea extends (_a56 = DeesElement, _chart_de
161492
161824
  if (changedProperties.has("goBright") && this.chart) {
161493
161825
  this.applyTheme();
161494
161826
  }
161495
- if (changedProperties.has("series") && this.chart && this.series.length > 0) {
161827
+ const seriesChanged = changedProperties.has("series") && this.series.length > 0;
161828
+ if (seriesChanged && this.chart) {
161496
161829
  await this.updateSeries(this.series);
161497
161830
  }
161498
161831
  if (changedProperties.has("yAxisFormatter") && this.chart) {
161499
161832
  }
161500
- if (changedProperties.has("realtimeMode") && this.chart) {
161501
- if (this.realtimeMode && this.rollingWindow > 0 && this.autoScrollInterval > 0) {
161502
- this.startAutoScroll();
161833
+ const realtimeConfigurationChanged = changedProperties.has("realtimeMode") || changedProperties.has("rollingWindow");
161834
+ const autoScrollConfigurationChanged = realtimeConfigurationChanged || changedProperties.has("autoScrollInterval");
161835
+ if (this.chart && realtimeConfigurationChanged) {
161836
+ if (!seriesChanged && this.internalChartData.length > 0) {
161837
+ await this.updateSeries(this.internalChartData);
161838
+ }
161839
+ if (this.realtimeMode && this.rollingWindow > 0) {
161840
+ await this.updateTimeWindow();
161503
161841
  } else {
161504
- this.stopAutoScroll();
161842
+ this.chart.timeScale().fitContent();
161505
161843
  }
161506
161844
  }
161507
- if (changedProperties.has("autoScrollInterval") && this.chart) {
161508
- this.stopAutoScroll();
161509
- if (this.realtimeMode && this.rollingWindow > 0 && this.autoScrollInterval > 0) {
161510
- this.startAutoScroll();
161511
- }
161845
+ if (this.chart && autoScrollConfigurationChanged) {
161846
+ this.syncAutoScroll();
161847
+ }
161848
+ if (changedProperties.has("selectedRange") || changedProperties.has("rangeSelectionEnabled")) {
161849
+ this.syncRangeOverlay();
161512
161850
  }
161513
161851
  if (changedProperties.has("chartLines") && this.chart) {
161514
161852
  this.refreshPriceLines();
@@ -161529,77 +161867,76 @@ var _DeesChartArea = class _DeesChartArea extends (_a56 = DeesElement, _chart_de
161529
161867
  async updateSeries(newSeries, animate = true) {
161530
161868
  if (!this.chart) return;
161531
161869
  try {
161532
- this.internalChartData = newSeries;
161533
- if (this.rollingWindow > 0 && this.realtimeMode) {
161534
- const now3 = Date.now();
161535
- const cutoffTime = now3 - this.rollingWindow;
161536
- if (newSeries.length !== this.seriesApis.size) {
161537
- this.recreateSeries(newSeries);
161538
- } else {
161539
- const isDark = !this.goBright;
161540
- newSeries.forEach((s9, index3) => {
161541
- const name = s9.name || `series-${index3}`;
161542
- const api = this.seriesApis.get(name);
161543
- if (!api) return;
161544
- const filtered = s9.data.filter((point4) => {
161545
- const ms = typeof point4.x === "number" ? point4.x : new Date(point4.x).getTime();
161546
- return ms > cutoffTime;
161547
- });
161548
- api.setData(this.convertDataToLC(filtered));
161549
- const color2 = this.resolveSeriesColor(s9, index3, isDark);
161550
- api.applyOptions({
161551
- topColor: this.colorToRgba(color2, isDark ? 0.4 : 0.5),
161552
- bottomColor: this.colorToRgba(color2, 0),
161553
- lineColor: color2
161554
- });
161555
- this.updatePriceLines(name, api, filtered, color2);
161556
- });
161557
- this.computeStats(newSeries);
161558
- }
161559
- try {
161560
- this.chart.timeScale().setVisibleRange({
161561
- from: Math.floor(cutoffTime / 1e3),
161562
- to: Math.floor(now3 / 1e3)
161563
- });
161564
- } catch (e10) {
161565
- }
161566
- if (this.yAxisScaling === "dynamic") {
161567
- const allValues = newSeries.flatMap(
161568
- (s9) => s9.data.filter((p6) => {
161569
- const ms = typeof p6.x === "number" ? p6.x : new Date(p6.x).getTime();
161570
- return ms > cutoffTime;
161571
- }).map((d5) => d5.y)
161572
- );
161573
- if (allValues.length > 0) {
161574
- const dynamicMax = Math.ceil(Math.max(...allValues) * 1.1);
161575
- for (const [, api] of this.seriesApis) {
161576
- api.applyOptions({
161577
- autoscaleInfoProvider: /* @__PURE__ */ __name(() => ({ priceRange: { minValue: 0, maxValue: dynamicMax } }), "autoscaleInfoProvider")
161578
- });
161579
- }
161580
- }
161870
+ void animate;
161871
+ const canonicalSeries = this.getCanonicalChartSeries(newSeries);
161872
+ this.internalChartData = canonicalSeries;
161873
+ const renderedKeys = [...this.seriesApis.keys()];
161874
+ const canonicalKeys = canonicalSeries.map((series, index3) => this.getSeriesKey(series, index3));
161875
+ if (!haveSameSeriesLayout(renderedKeys, canonicalKeys)) {
161876
+ this.clearCrosshairForDataReplacement();
161877
+ this.recreateSeries(canonicalSeries);
161878
+ this.updateSeriesPresentation(canonicalSeries);
161879
+ return;
161880
+ }
161881
+ if (!this.realtimeMode || this.rollingWindow <= 0) {
161882
+ this.clearCrosshairForDataReplacement();
161883
+ canonicalSeries.forEach((series, index3) => {
161884
+ this.seriesApis.get(this.getSeriesKey(series, index3)).setData(this.convertDataToLC(series.data));
161885
+ });
161886
+ this.pendingRealtimePrune = false;
161887
+ this.updateSeriesPresentation(canonicalSeries);
161888
+ return;
161889
+ }
161890
+ const plannedUpdates = canonicalSeries.map((series, index3) => {
161891
+ const api = this.seriesApis.get(this.getSeriesKey(series, index3));
161892
+ const canonicalData = this.convertDataToLC(series.data).map((point4) => ({ time: point4.time, value: point4.value }));
161893
+ return {
161894
+ api,
161895
+ canonicalData,
161896
+ plan: planRealtimeChartUpdates(this.getRenderedSeriesData(api), canonicalData)
161897
+ };
161898
+ });
161899
+ if (plannedUpdates.some(({ plan }) => plan.replacementRequired)) {
161900
+ this.clearCrosshairForDataReplacement();
161901
+ for (const { api, canonicalData } of plannedUpdates) {
161902
+ api.setData(canonicalData.map((point4) => ({
161903
+ time: point4.time,
161904
+ value: point4.value
161905
+ })));
161581
161906
  }
161907
+ this.pendingRealtimePrune = false;
161582
161908
  } else {
161583
- if (newSeries.length !== this.seriesApis.size) {
161584
- this.recreateSeries(newSeries);
161585
- } else {
161586
- const isDark = !this.goBright;
161587
- newSeries.forEach((s9, index3) => {
161588
- const name = s9.name || `series-${index3}`;
161589
- const api = this.seriesApis.get(name);
161590
- if (!api) return;
161591
- api.setData(this.convertDataToLC(s9.data));
161592
- const color2 = this.resolveSeriesColor(s9, index3, isDark);
161593
- api.applyOptions({
161594
- topColor: this.colorToRgba(color2, isDark ? 0.4 : 0.5),
161595
- bottomColor: this.colorToRgba(color2, 0),
161596
- lineColor: color2
161597
- });
161598
- this.updatePriceLines(name, api, s9.data, color2);
161599
- });
161600
- this.computeStats(newSeries);
161601
- }
161602
- }
161909
+ for (const { api, plan } of plannedUpdates) {
161910
+ for (const update of plan.updates) {
161911
+ api.update({
161912
+ time: update.point.time,
161913
+ value: update.point.value
161914
+ }, update.historicalUpdate);
161915
+ }
161916
+ }
161917
+ const pruneRequired = plannedUpdates.some(({ plan }) => plan.pruneRequired);
161918
+ const pruneRecommended = plannedUpdates.some(({ plan }) => plan.pruneRecommended);
161919
+ if (pruneRequired) {
161920
+ this.clearCrosshairForDataReplacement();
161921
+ for (const { api, canonicalData } of plannedUpdates) {
161922
+ api.setData(canonicalData.map((point4) => ({
161923
+ time: point4.time,
161924
+ value: point4.value
161925
+ })));
161926
+ }
161927
+ } else if (pruneRecommended && this.crosshairActive) {
161928
+ this.pendingRealtimePrune = true;
161929
+ } else if (pruneRecommended) {
161930
+ for (const { api, canonicalData } of plannedUpdates) {
161931
+ api.setData(canonicalData.map((point4) => ({
161932
+ time: point4.time,
161933
+ value: point4.value
161934
+ })));
161935
+ }
161936
+ this.pendingRealtimePrune = false;
161937
+ }
161938
+ }
161939
+ this.updateSeriesPresentation(canonicalSeries);
161603
161940
  } catch (error) {
161604
161941
  console.error("Failed to update chart series:", error);
161605
161942
  }
@@ -161618,15 +161955,26 @@ var _DeesChartArea = class _DeesChartArea extends (_a56 = DeesElement, _chart_de
161618
161955
  }
161619
161956
  async appendData(newData) {
161620
161957
  if (!this.chart) return;
161958
+ const nextSeries = this.internalChartData.map((series) => ({
161959
+ ...series,
161960
+ data: [...series.data]
161961
+ }));
161962
+ const namedOccurrences = /* @__PURE__ */ new Map();
161621
161963
  newData.forEach((s9, index3) => {
161622
- const name = s9.name || `series-${index3}`;
161623
- const api = this.seriesApis.get(name);
161624
- if (!api || s9.data.length === 0) return;
161625
- for (const point4 of s9.data) {
161626
- const lcPoints = this.convertDataToLC([point4]);
161627
- if (lcPoints.length > 0) api.update(lcPoints[0]);
161628
- }
161964
+ let targetIndex = index3;
161965
+ if (s9.name) {
161966
+ const occurrence = namedOccurrences.get(s9.name) ?? 0;
161967
+ namedOccurrences.set(s9.name, occurrence + 1);
161968
+ const matchingIndices = this.internalChartData.map((series, seriesIndex) => series.name === s9.name ? seriesIndex : -1).filter((seriesIndex) => seriesIndex >= 0);
161969
+ const matchingIndex = matchingIndices[occurrence];
161970
+ if (matchingIndex === void 0) return;
161971
+ targetIndex = matchingIndex;
161972
+ }
161973
+ const targetSeries = nextSeries[targetIndex];
161974
+ if (!targetSeries || s9.data.length === 0) return;
161975
+ targetSeries.data.push(...s9.data);
161629
161976
  });
161977
+ await this.updateSeries(nextSeries);
161630
161978
  }
161631
161979
  async updateOptions(options2) {
161632
161980
  if (!this.chart) return;
@@ -161663,25 +162011,31 @@ var _DeesChartArea = class _DeesChartArea extends (_a56 = DeesElement, _chart_de
161663
162011
  }
161664
162012
  refreshPriceLines() {
161665
162013
  const isDark = !this.goBright;
161666
- this.chartSeries.forEach((s9, index3) => {
161667
- const name = s9.name || `series-${index3}`;
161668
- const api = this.seriesApis.get(name);
162014
+ this.getCanonicalChartSeries(this.internalChartData).forEach((s9, index3) => {
162015
+ const seriesKey = this.getSeriesKey(s9, index3);
162016
+ const api = this.seriesApis.get(seriesKey);
161669
162017
  if (!api) return;
161670
- this.updatePriceLines(name, api, s9.data, this.resolveSeriesColor(s9, index3, isDark));
162018
+ this.updatePriceLines(seriesKey, api, s9.data, this.resolveSeriesColor(s9, index3, isDark));
161671
162019
  });
161672
162020
  }
161673
162021
  startAutoScroll() {
161674
- if (this.autoScrollTimer) return;
162022
+ if (this.autoScrollTimer !== null) return;
161675
162023
  this.autoScrollTimer = window.setInterval(() => {
161676
162024
  this.updateTimeWindow();
161677
162025
  }, this.autoScrollInterval);
161678
162026
  }
161679
162027
  stopAutoScroll() {
161680
- if (this.autoScrollTimer) {
162028
+ if (this.autoScrollTimer !== null) {
161681
162029
  window.clearInterval(this.autoScrollTimer);
161682
162030
  this.autoScrollTimer = null;
161683
162031
  }
161684
162032
  }
162033
+ syncAutoScroll() {
162034
+ this.stopAutoScroll();
162035
+ if (this.realtimeMode && this.rollingWindow > 0 && this.autoScrollInterval > 0) {
162036
+ this.startAutoScroll();
162037
+ }
162038
+ }
161685
162039
  };
161686
162040
  _init57 = __decoratorStart(_a56);
161687
162041
  _chart = new WeakMap();
@@ -161698,6 +162052,8 @@ _autoScrollInterval = new WeakMap();
161698
162052
  _legendStats = new WeakMap();
161699
162053
  _hideLegend = new WeakMap();
161700
162054
  _chartLines = new WeakMap();
162055
+ _rangeSelectionEnabled = new WeakMap();
162056
+ _selectedRange = new WeakMap();
161701
162057
  __decorateElement(_init57, 4, "chart", _chart_dec, _DeesChartArea, _chart);
161702
162058
  __decorateElement(_init57, 4, "seriesStats", _seriesStats_dec, _DeesChartArea, _seriesStats);
161703
162059
  __decorateElement(_init57, 4, "isFullPage", _isFullPage_dec, _DeesChartArea, _isFullPage);
@@ -161712,6 +162068,8 @@ __decorateElement(_init57, 4, "autoScrollInterval", _autoScrollInterval_dec, _De
161712
162068
  __decorateElement(_init57, 4, "legendStats", _legendStats_dec, _DeesChartArea, _legendStats);
161713
162069
  __decorateElement(_init57, 4, "hideLegend", _hideLegend_dec, _DeesChartArea, _hideLegend);
161714
162070
  __decorateElement(_init57, 4, "chartLines", _chartLines_dec, _DeesChartArea, _chartLines);
162071
+ __decorateElement(_init57, 4, "rangeSelectionEnabled", _rangeSelectionEnabled_dec, _DeesChartArea, _rangeSelectionEnabled);
162072
+ __decorateElement(_init57, 4, "selectedRange", _selectedRange_dec, _DeesChartArea, _selectedRange);
161715
162073
  _DeesChartArea = __decorateElement(_init57, 0, "DeesChartArea", _DeesChartArea_decorators, _DeesChartArea);
161716
162074
  __name(_DeesChartArea, "DeesChartArea");
161717
162075
  __publicField(_DeesChartArea, "demo", demoFunc37);
@@ -198026,7 +198384,7 @@ init_group_runtime();
198026
198384
  // ts_web/00_commitinfo_data.ts
198027
198385
  var commitinfo = {
198028
198386
  name: "@design.estate/dees-catalog",
198029
- version: "3.98.0",
198387
+ version: "3.99.0",
198030
198388
  description: "A comprehensive library that provides dynamic web components for building sophisticated and modern web applications using JavaScript and TypeScript."
198031
198389
  };
198032
198390