@trdrs/quickcharts 2.2.0 → 2.4.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.
package/dist/index.js CHANGED
@@ -110,6 +110,9 @@ import {
110
110
  ICON_CATEGORIES,
111
111
  isEmojiGlyph
112
112
  } from "./chunk-QYGSDD2Q.js";
113
+ import {
114
+ symbolNames
115
+ } from "./chunk-GFQQSY47.js";
113
116
 
114
117
  // src/datafeed.ts
115
118
  function olderPageVerdict(page, askedTo, alreadyHopped) {
@@ -3079,7 +3082,8 @@ var FEATURE_KEYS = {
3079
3082
  replay: "flag",
3080
3083
  history: "flag",
3081
3084
  sessions: "flag",
3082
- crosshair: "flag"
3085
+ crosshair: "flag",
3086
+ navigation: "flag"
3083
3087
  };
3084
3088
  var TOP_BAR_KEYS = {
3085
3089
  symbol: "flag",
@@ -3108,7 +3112,9 @@ var UI_KEYS = {
3108
3112
  toasts: "flag",
3109
3113
  symbolSearch: "flag",
3110
3114
  indicatorPicker: "flag",
3111
- indicatorSettings: "flag"
3115
+ indicatorSettings: "flag",
3116
+ priceScale: "flag",
3117
+ crosshair: { horizontal: "flag", labels: "flag", solid: "flag" }
3112
3118
  };
3113
3119
  function checkKeys(path, value, keys) {
3114
3120
  if (value === void 0) return;
@@ -3136,7 +3142,8 @@ function resolveFeatures(config) {
3136
3142
  replay: on(config?.replay),
3137
3143
  history: on(config?.history),
3138
3144
  sessions: on(config?.sessions),
3139
- crosshair: on(config?.crosshair)
3145
+ crosshair: on(config?.crosshair),
3146
+ navigation: on(config?.navigation)
3140
3147
  };
3141
3148
  }
3142
3149
  var shown = (value) => value !== false;
@@ -3152,6 +3159,9 @@ function resolveUi(config, features, offered = {}) {
3152
3159
  const inLegend = (key) => legend2 && (typeof legendConfig !== "object" || shown(legendConfig[key]));
3153
3160
  const symbolSearch = shown(config?.symbolSearch);
3154
3161
  const indicatorPicker = shown(config?.indicatorPicker);
3162
+ const crosshairConfig = config?.crosshair;
3163
+ const crosshair = features.crosshair && shown(crosshairConfig);
3164
+ const inCrosshair = (key) => crosshair && (typeof crosshairConfig !== "object" || shown(crosshairConfig[key]));
3155
3165
  return {
3156
3166
  topBar,
3157
3167
  symbolPill: inBar("symbol") && symbolSearch,
@@ -3175,13 +3185,19 @@ function resolveUi(config, features, offered = {}) {
3175
3185
  legend: legend2,
3176
3186
  legendValues: inLegend("values"),
3177
3187
  marketStatus: inLegend("marketStatus"),
3178
- navigation: shown(config?.navigation),
3188
+ navigation: features.navigation && shown(config?.navigation),
3179
3189
  contextMenu: shown(config?.contextMenu),
3180
3190
  replayTransport: features.replay && shown(config?.replayTransport),
3181
3191
  toasts: shown(config?.toasts),
3182
3192
  symbolSearch,
3183
3193
  indicatorPicker,
3184
- indicatorSettings: shown(config?.indicatorSettings)
3194
+ indicatorSettings: shown(config?.indicatorSettings),
3195
+ priceScale: shown(config?.priceScale),
3196
+ crosshair,
3197
+ crosshairHorizontal: inCrosshair("horizontal"),
3198
+ crosshairLabels: inCrosshair("labels"),
3199
+ // Solid is the one part that is off unless named: a crosshair is dashed by default.
3200
+ crosshairSolid: crosshair && typeof crosshairConfig === "object" && crosshairConfig.solid === true
3185
3201
  };
3186
3202
  }
3187
3203
  function canCopyImage() {
@@ -3310,6 +3326,29 @@ function styleOptions(style, paint) {
3310
3326
  return candleOptions(paint);
3311
3327
  }
3312
3328
  }
3329
+ function fadedStyleOptions(style, paint, alpha) {
3330
+ const a = Math.min(1, Math.max(0, alpha));
3331
+ const up = paint.appearance.upColor;
3332
+ const down = paint.appearance.downColor;
3333
+ switch (style) {
3334
+ case "line":
3335
+ case "stepline":
3336
+ return { color: withAlpha2(paint.canvas.neutral, a) };
3337
+ case "area":
3338
+ return { lineColor: withAlpha2(up, a), topColor: withAlpha2(up, 0.28 * a), bottomColor: withAlpha2(up, 0.04 * a) };
3339
+ case "baseline":
3340
+ return {
3341
+ topLineColor: withAlpha2(up, a),
3342
+ bottomLineColor: withAlpha2(down, a),
3343
+ topFillColor1: withAlpha2(up, 0.28 * a),
3344
+ topFillColor2: withAlpha2(up, 0.04 * a),
3345
+ bottomFillColor1: withAlpha2(down, 0.04 * a),
3346
+ bottomFillColor2: withAlpha2(down, 0.28 * a)
3347
+ };
3348
+ default:
3349
+ return styleOptions(style, paint);
3350
+ }
3351
+ }
3313
3352
  function lineOptions(paint) {
3314
3353
  return { color: paint.canvas.neutral, lineWidth: 2 };
3315
3354
  }
@@ -4960,6 +4999,7 @@ import {
4960
4999
  createChart as createRenderer,
4961
5000
  HistogramSeries as HistogramSeries2,
4962
5001
  LineSeries as LineSeries4,
5002
+ LineStyle as LineStyle3,
4963
5003
  TrackingModeExitMode
4964
5004
  } from "lightweight-charts";
4965
5005
 
@@ -5014,6 +5054,59 @@ function coerceScaleMode(raw) {
5014
5054
  return SCALE_MODES.includes(raw ?? "") ? raw : "normal";
5015
5055
  }
5016
5056
 
5057
+ // src/widget/styleMorph.ts
5058
+ var STYLE_MORPH_MS = 500;
5059
+ function morphEase(t) {
5060
+ const clamped = Math.min(1, Math.max(0, t));
5061
+ return (1 - Math.cos(Math.PI * clamped)) / 2;
5062
+ }
5063
+ function foldBar(bar, fold) {
5064
+ const keep = 1 - Math.min(1, Math.max(0, fold));
5065
+ return {
5066
+ time: bar.t,
5067
+ open: bar.c + (bar.o - bar.c) * keep,
5068
+ high: bar.c + (bar.h - bar.c) * keep,
5069
+ low: bar.c + (bar.l - bar.c) * keep,
5070
+ close: bar.c
5071
+ };
5072
+ }
5073
+ function startStyleMorph(deps) {
5074
+ const frame = deps.frame ?? ((tick2) => requestAnimationFrame(tick2));
5075
+ const cancel = deps.cancelFrame ?? ((handle2) => cancelAnimationFrame(handle2));
5076
+ const now = deps.now ?? (() => performance.now());
5077
+ const start = now();
5078
+ let handle = null;
5079
+ let ended = false;
5080
+ const draw = (t) => {
5081
+ const p = morphEase(t);
5082
+ const fold = deps.direction === "fold" ? p : 1 - p;
5083
+ deps.bars.setData(deps.shown().map((bar) => foldBar(bar, fold)));
5084
+ deps.fadeLine(fold);
5085
+ };
5086
+ const finish = () => {
5087
+ if (ended) return;
5088
+ ended = true;
5089
+ if (handle !== null) cancel(handle);
5090
+ handle = null;
5091
+ draw(1);
5092
+ deps.done();
5093
+ };
5094
+ const tick = () => {
5095
+ handle = null;
5096
+ if (ended) return;
5097
+ const t = (now() - start) / STYLE_MORPH_MS;
5098
+ if (t >= 1) {
5099
+ finish();
5100
+ return;
5101
+ }
5102
+ draw(t);
5103
+ handle = frame(tick);
5104
+ };
5105
+ draw(0);
5106
+ handle = frame(tick);
5107
+ return { running: () => !ended, finish };
5108
+ }
5109
+
5017
5110
  // src/widget/baselineLevel.ts
5018
5111
  var BASELINE_SCREEN_PERCENT = 50;
5019
5112
  var BASELINE_DRIFT_PX = 0.5;
@@ -5707,8 +5800,8 @@ function bundledGlyphSource(glyph2) {
5707
5800
  }
5708
5801
 
5709
5802
  // src/pointerInput.ts
5710
- function pointerLock(locked) {
5711
- const on = !locked;
5803
+ function pointerLock(locked, navigable = true) {
5804
+ const on = !locked && navigable;
5712
5805
  return {
5713
5806
  handleScroll: on,
5714
5807
  handleScale: { mouseWheel: on, pinch: false, axisPressedMouseMove: on, axisDoubleClickReset: on },
@@ -6822,7 +6915,7 @@ function attachDrawings(options) {
6822
6915
  const locked = () => allLocked || workflow().allLocked === true;
6823
6916
  const changed = () => events.onChange?.();
6824
6917
  const lockPointer = (locked2) => {
6825
- const lock = pointerLock(locked2);
6918
+ const lock = pointerLock(locked2, options.navigable ?? true);
6826
6919
  chart.applyOptions({ handleScroll: lock.handleScroll, handleScale: lock.handleScale });
6827
6920
  container.style.touchAction = lock.touchAction;
6828
6921
  };
@@ -10336,6 +10429,7 @@ function attachDrawingsPlane(deps) {
10336
10429
  const handle = attachDrawings({
10337
10430
  chart: deps.chart,
10338
10431
  series: deps.series,
10432
+ navigable: deps.navigable,
10339
10433
  container: deps.container,
10340
10434
  symbol: deps.symbol,
10341
10435
  timeframe: deps.timeframe,
@@ -12584,42 +12678,6 @@ function attachCompare(chart, deps) {
12584
12678
  return handle;
12585
12679
  }
12586
12680
 
12587
- // src/symbolLabel.ts
12588
- function bareTicker(symbol) {
12589
- return /^[A-Za-z][A-Za-z0-9._-]*:([A-Za-z0-9][A-Za-z0-9._!]*)$/.exec(symbol)?.[1] ?? symbol;
12590
- }
12591
- var SLASH_PAIR = /^[A-Za-z][A-Za-z0-9.]*\/[A-Za-z][A-Za-z0-9.]*$/;
12592
- function symbolLabel(symbol) {
12593
- const ticker = bareTicker(symbol);
12594
- return SLASH_PAIR.test(ticker) ? ticker.replace("/", "") : ticker;
12595
- }
12596
- var PAIR_MARKETS = /* @__PURE__ */ new Set(["crypto", "forex"]);
12597
- function pairLabel(ticker, type, currencyCode) {
12598
- const quote = currencyCode?.trim();
12599
- if (!quote || !PAIR_MARKETS.has(type.toLowerCase())) return null;
12600
- const bare = bareTicker(ticker);
12601
- const base = bare.length > quote.length && bare.toUpperCase().endsWith(quote.toUpperCase()) ? bare.slice(0, -quote.length) : bare;
12602
- return base && base.toUpperCase() !== quote.toUpperCase() ? `${base} / ${quote}` : null;
12603
- }
12604
- function factsOf(symbol) {
12605
- if (typeof symbol === "string") return { ticker: symbol, type: "", shortName: "", longName: "", currencyCode: void 0 };
12606
- if ("symbol" in symbol) return { ticker: symbol.symbol, type: symbol.type, shortName: "", longName: symbol.name.trim(), currencyCode: symbol.currencyCode };
12607
- const name2 = symbol.name.trim();
12608
- const description = symbol.description.trim();
12609
- return { ticker: symbol.ticker, type: symbol.type, shortName: name2 !== description ? name2 : "", longName: description, currencyCode: symbol.currencyCode };
12610
- }
12611
- function symbolNames(symbol) {
12612
- const facts = factsOf(symbol);
12613
- if (facts.type.toLowerCase() === "spread") return { mark: facts.ticker, title: facts.ticker, description: facts.ticker };
12614
- const bare = bareTicker(facts.ticker);
12615
- const pair = pairLabel(facts.ticker, facts.type, facts.currencyCode) ?? (SLASH_PAIR.test(bare) ? bare.replace("/", " / ") : null);
12616
- return {
12617
- mark: pair ? pair.replace(" / ", "") : symbolLabel(facts.ticker),
12618
- title: pair ?? (facts.shortName || bare),
12619
- description: pair ?? (facts.longName || bare)
12620
- };
12621
- }
12622
-
12623
12681
  // src/widget/compare.ts
12624
12682
  var CHIP_THROTTLE_MS = 250;
12625
12683
  function attachComparePlane(deps) {
@@ -12787,6 +12845,7 @@ function composeFormingBar(parent, subs, k) {
12787
12845
  }
12788
12846
 
12789
12847
  // src/widget/replay.ts
12848
+ var START_HOLD_MS = 250;
12790
12849
  function coerceReplaySpeed(raw) {
12791
12850
  const n = Number(raw);
12792
12851
  return REPLAY_SPEEDS.includes(n) ? n : 10;
@@ -12805,6 +12864,13 @@ function attachReplayPlane(deps) {
12805
12864
  let stepping = null;
12806
12865
  let generation = 0;
12807
12866
  let destroyed = false;
12867
+ let glide = null;
12868
+ const stopGlide = () => {
12869
+ if (glide) {
12870
+ clearTimeout(glide);
12871
+ glide = null;
12872
+ }
12873
+ };
12808
12874
  const invalidateStep = () => {
12809
12875
  generation += 1;
12810
12876
  stepping = null;
@@ -12825,15 +12891,34 @@ function attachReplayPlane(deps) {
12825
12891
  const currentGrain = () => autoGrain ? "auto" : manualGrain ?? "auto";
12826
12892
  const paintCursor = () => {
12827
12893
  if (!master) return;
12894
+ stopGlide();
12828
12895
  deps.paint(master.slice(0, cursor));
12829
12896
  deps.chart.timeScale().scrollToRealTime();
12830
12897
  };
12831
12898
  const paintForming = () => {
12832
12899
  if (!master || !subs) return;
12900
+ stopGlide();
12833
12901
  const parent = master[cursor - 1];
12834
12902
  deps.paint([...master.slice(0, cursor - 1), composeFormingBar(parent, subs, formK)]);
12835
12903
  deps.chart.timeScale().scrollToRealTime();
12836
12904
  };
12905
+ const paintStart = () => {
12906
+ if (!master) return;
12907
+ const scale = deps.chart.timeScale();
12908
+ const shown2 = scale.getVisibleRange();
12909
+ const view = scale.getVisibleLogicalRange();
12910
+ const chosen = master[cursor - 1].t;
12911
+ const from = shown2?.from;
12912
+ const to = shown2?.to;
12913
+ if (!view || typeof from !== "number" || typeof to !== "number" || chosen < from || chosen > to) return paintCursor();
12914
+ stopGlide();
12915
+ deps.paint(master.slice(0, cursor));
12916
+ scale.setVisibleLogicalRange(view);
12917
+ glide = setTimeout(() => {
12918
+ glide = null;
12919
+ if (master && !destroyed && !deps.disposed()) scale.scrollToRealTime();
12920
+ }, START_HOLD_MS);
12921
+ };
12837
12922
  const stopTimer = () => {
12838
12923
  if (timer) {
12839
12924
  clearInterval(timer);
@@ -12917,6 +13002,7 @@ function attachReplayPlane(deps) {
12917
13002
  };
12918
13003
  function abandon() {
12919
13004
  invalidateStep();
13005
+ stopGlide();
12920
13006
  if (!master) return;
12921
13007
  stopTimer();
12922
13008
  playing = false;
@@ -12944,7 +13030,7 @@ function attachReplayPlane(deps) {
12944
13030
  const idx = master.findIndex((b) => b.t >= atSec);
12945
13031
  cursor = Math.max(2, (idx === -1 ? master.length - 1 : idx) + 1);
12946
13032
  deps.setHeader(true);
12947
- paintCursor();
13033
+ paintStart();
12948
13034
  sync();
12949
13035
  },
12950
13036
  exit() {
@@ -13467,7 +13553,7 @@ function attachExtensionsPlane(deps) {
13467
13553
  },
13468
13554
  lockPanZoom: (locked) => {
13469
13555
  if (deps.disposed()) return;
13470
- const state = pointerLock(locked);
13556
+ const state = pointerLock(locked, deps.navigable);
13471
13557
  deps.chart.applyOptions({ handleScroll: state.handleScroll, handleScale: state.handleScale });
13472
13558
  deps.setTouchAction(state.touchAction);
13473
13559
  }
@@ -15976,10 +16062,11 @@ function registerChartCommands(deps) {
15976
16062
  });
15977
16063
  add({ id: "chart.view.reset", scope: "chart", label: "command.viewReset", shortcut: "Alt+KeyR", available: always, execute: () => handle.reset() });
15978
16064
  add({ id: "chart.view.goLive", scope: "chart", label: "command.viewGoLive", shortcut: "Alt+KeyL", available: always, execute: () => handle.goLive() });
15979
- add({ id: "chart.view.zoomIn", scope: "chart", label: "command.viewZoomIn", shortcut: "Equal", available: always, execute: () => deps.zoom("in") });
15980
- add({ id: "chart.view.zoomOut", scope: "chart", label: "command.viewZoomOut", shortcut: "Minus", available: always, execute: () => deps.zoom("out") });
15981
- add({ id: "chart.view.scrollLeft", scope: "chart", label: "command.viewScrollLeft", shortcut: "ArrowLeft", available: always, execute: () => deps.scroll("left") });
15982
- add({ id: "chart.view.scrollRight", scope: "chart", label: "command.viewScrollRight", shortcut: "ArrowRight", available: always, execute: () => deps.scroll("right") });
16065
+ const navigable = () => features.navigation;
16066
+ add({ id: "chart.view.zoomIn", scope: "chart", label: "command.viewZoomIn", shortcut: "Equal", available: navigable, execute: () => deps.zoom("in") });
16067
+ add({ id: "chart.view.zoomOut", scope: "chart", label: "command.viewZoomOut", shortcut: "Minus", available: navigable, execute: () => deps.zoom("out") });
16068
+ add({ id: "chart.view.scrollLeft", scope: "chart", label: "command.viewScrollLeft", shortcut: "ArrowLeft", available: navigable, execute: () => deps.scroll("left") });
16069
+ add({ id: "chart.view.scrollRight", scope: "chart", label: "command.viewScrollRight", shortcut: "ArrowRight", available: navigable, execute: () => deps.scroll("right") });
15983
16070
  add({
15984
16071
  id: "chart.data.download",
15985
16072
  scope: "chart",
@@ -16783,14 +16870,21 @@ function createChartInstance(deps) {
16783
16870
  vertLines: { color: canvas().grid, visible: eff.appearance.grid },
16784
16871
  horzLines: { color: canvas().grid, visible: eff.appearance.grid }
16785
16872
  },
16786
- crosshair: { mode: deps.features.crosshair ? CrosshairMode.Normal : CrosshairMode.Hidden },
16873
+ crosshair: {
16874
+ mode: deps.ui.crosshair ? CrosshairMode.Normal : CrosshairMode.Hidden,
16875
+ vertLine: { labelVisible: deps.ui.crosshairLabels, ...deps.ui.crosshairSolid ? { style: LineStyle3.Solid } : {} },
16876
+ horzLine: { visible: deps.ui.crosshairHorizontal, labelVisible: deps.ui.crosshairLabels, ...deps.ui.crosshairSolid ? { style: LineStyle3.Solid } : {} }
16877
+ },
16787
16878
  // A finger held on the plot scrubs the crosshair for as long as it stays down, and lifting it
16788
16879
  // takes the crosshair away, so the next one-finger drag pans: the way a native chart reads.
16789
16880
  trackingMode: { exitMode: TrackingModeExitMode.OnTouchEnd },
16790
16881
  // The renderer's own momentum throws a flick as fast as seven pixels a millisecond and coasts
16791
16882
  // it thousands of pixels; the chart coasts a flick itself, calmly.
16792
16883
  kineticScroll: { touch: false, mouse: false },
16793
- rightPriceScale: { borderVisible: false, scaleMargins: { top: 0.08, bottom: 0.08 } },
16884
+ // A chart that holds its view takes no drag, wheel, pinch or scale gesture from the renderer.
16885
+ handleScroll: deps.features.navigation,
16886
+ handleScale: deps.features.navigation,
16887
+ rightPriceScale: { visible: deps.ui.priceScale, borderVisible: false, scaleMargins: { top: 0.08, bottom: 0.08 } },
16794
16888
  timeScale: { borderVisible: false, timeVisible: true, secondsVisible: false, rightOffset: 4, barSpacing: 8, minBarSpacing: 0.5 }
16795
16889
  });
16796
16890
  const anchor = chart.addSeries(LineSeries4, {
@@ -16799,7 +16893,13 @@ function createChartInstance(deps) {
16799
16893
  priceLineVisible: false,
16800
16894
  crosshairMarkerVisible: false
16801
16895
  });
16802
- let series2 = addStyleSeries(chart, style, paint());
16896
+ const addMainSeries = (id) => {
16897
+ const added = addStyleSeries(chart, id, paint());
16898
+ if (!deps.ui.priceScale) added.applyOptions({ priceLineVisible: false, lastValueVisible: false });
16899
+ return added;
16900
+ };
16901
+ let series2 = addMainSeries(style);
16902
+ let morph = null;
16803
16903
  const baselineLevel = createBaselineLevel({ paneHeight: () => chart.paneSize().height });
16804
16904
  if (style === "baseline") baselineLevel.follow(series2);
16805
16905
  const volume2 = chart.addSeries(HistogramSeries2, {
@@ -16930,15 +17030,17 @@ function createChartInstance(deps) {
16930
17030
  const crosshairSubs = /* @__PURE__ */ new Set();
16931
17031
  const timeClickSubs = /* @__PURE__ */ new Set();
16932
17032
  const rangeSubs = /* @__PURE__ */ new Set();
17033
+ const reducedMotion = () => gestures.ownerDocument.defaultView?.matchMedia?.("(prefers-reduced-motion: reduce)").matches ?? false;
16933
17034
  const ranges = createRangeApi({
16934
17035
  chart,
16935
17036
  disposed: disposedFn,
16936
17037
  muted,
16937
17038
  mirrored,
16938
17039
  onGlide: () => followLiveEdge(),
16939
- reducedMotion: () => gestures.ownerDocument.defaultView?.matchMedia?.("(prefers-reduced-motion: reduce)").matches ?? false
17040
+ reducedMotion
16940
17041
  });
16941
- const fling = attachFling({ chart, target: gestures });
17042
+ const still = { stop: () => void 0, destroy: () => void 0 };
17043
+ const fling = deps.features.navigation ? attachFling({ chart, target: gestures }) : still;
16942
17044
  let unregisterRangeMirror = () => void 0;
16943
17045
  function applyTimezone() {
16944
17046
  if (disposed) return;
@@ -17046,6 +17148,7 @@ function createChartInstance(deps) {
17046
17148
  const drawings2 = attachDrawingsPlane({
17047
17149
  chart,
17048
17150
  series: anchor,
17151
+ navigable: deps.features.navigation,
17049
17152
  container: gestures,
17050
17153
  chrome: chrome2,
17051
17154
  chartId: deps.id,
@@ -17119,7 +17222,7 @@ function createChartInstance(deps) {
17119
17222
  setHeader: () => legend2.setHeader(symbol, tf),
17120
17223
  // A chart whose crosshair the host switched OFF keeps it off: replay may take the crosshair
17121
17224
  // away for the length of a question, never hand one back that was never there.
17122
- setCrosshair: (visible) => chart.applyOptions({ crosshair: { mode: visible && deps.features.crosshair ? CrosshairMode.Normal : CrosshairMode.Hidden } }),
17225
+ setCrosshair: (visible) => chart.applyOptions({ crosshair: { mode: visible && deps.ui.crosshair ? CrosshairMode.Normal : CrosshairMode.Hidden } }),
17123
17226
  // The feed's own grains: a chart never offers, or fetches, a timeframe its feed cannot serve.
17124
17227
  resolutions: () => deps.capabilities().resolutions,
17125
17228
  persist: (key, value) => storage.set(key === "speed" ? REPLAY_SPEED_KEY : REPLAY_TIMEFRAME_KEY, value),
@@ -17153,6 +17256,7 @@ function createChartInstance(deps) {
17153
17256
  const extensions = attachExtensionsPlane({
17154
17257
  chartId: deps.id,
17155
17258
  chart,
17259
+ navigable: deps.features.navigation,
17156
17260
  series: () => anchor,
17157
17261
  visible: () => series2,
17158
17262
  gestures,
@@ -17260,8 +17364,8 @@ function createChartInstance(deps) {
17260
17364
  navigationRoot.addEventListener("mouseup", onNativeDragEnd, { passive: true });
17261
17365
  navigationRoot.addEventListener("pointerup", onNativeDragEnd, { passive: true });
17262
17366
  navigationRoot.addEventListener("pointercancel", onNativeDragEnd, { passive: true });
17263
- const pinch = attachPinch({ chart, target: gestures });
17264
- const freePan = deps.freePan ? attachFreePan({
17367
+ const pinch = deps.features.navigation ? attachPinch({ chart, target: gestures }) : null;
17368
+ const freePan = deps.freePan && deps.features.navigation ? attachFreePan({
17265
17369
  chart,
17266
17370
  target: gestures,
17267
17371
  framing: () => priceAxisPolicy() === "auto",
@@ -17285,9 +17389,11 @@ function createChartInstance(deps) {
17285
17389
  const painted = shownBars();
17286
17390
  const values = painted.map((b) => ({ time: b.t, value: b.c }));
17287
17391
  anchor.setData(values);
17288
- series2.setData(
17289
- shaped ? values : painted.map((b) => ({ time: b.t, open: b.o, high: b.h, low: b.l, close: b.c }))
17290
- );
17392
+ if (morph?.bars !== series2) {
17393
+ series2.setData(
17394
+ shaped ? values : painted.map((b) => ({ time: b.t, open: b.o, high: b.h, low: b.l, close: b.c }))
17395
+ );
17396
+ }
17291
17397
  if (volumeInstance()) paintVolume();
17292
17398
  indicators2.recompute();
17293
17399
  compare?.sync();
@@ -17613,20 +17719,48 @@ function createChartInstance(deps) {
17613
17719
  function setStyle(next) {
17614
17720
  if (disposed || next === style || !deps.styles.includes(next)) return;
17615
17721
  const keep = chart.timeScale().getVisibleLogicalRange();
17722
+ morph?.run.finish();
17616
17723
  const previous = series2;
17724
+ const previousStyle = style;
17617
17725
  style = next;
17618
17726
  storage.set(STYLE_KEY, next);
17619
- series2 = addStyleSeries(chart, next, paint());
17727
+ series2 = addMainSeries(next);
17620
17728
  baselineLevel.follow(next === "baseline" ? series2 : null);
17621
17729
  countdown?.seriesChanged(previous);
17622
17730
  extensions.visibleSeriesReplaced();
17623
17731
  applyPriceFormat();
17624
- try {
17625
- chart.removeSeries(previous);
17626
- } catch {
17627
- }
17732
+ const remove = (leaving) => {
17733
+ try {
17734
+ chart.removeSeries(leaving);
17735
+ } catch {
17736
+ }
17737
+ };
17738
+ const folding = valueShaped(next) && !valueShaped(previousStyle);
17739
+ const unfolding = !valueShaped(next) && valueShaped(previousStyle);
17740
+ const morphing = !!deps.styleMorph && (folding || unfolding) && !reducedMotion();
17741
+ if (!morphing) remove(previous);
17628
17742
  paintAll();
17629
17743
  if (keep) chart.timeScale().setVisibleLogicalRange(keep);
17744
+ if (morphing) {
17745
+ const bars2 = folding ? previous : series2;
17746
+ const line2 = folding ? series2 : previous;
17747
+ const lineStyle2 = folding ? next : previousStyle;
17748
+ const entry = {
17749
+ bars: bars2,
17750
+ run: startStyleMorph({
17751
+ bars: bars2,
17752
+ fadeLine: (alpha) => line2.applyOptions(fadedStyleOptions(lineStyle2, paint(), alpha)),
17753
+ shown: shownBars,
17754
+ direction: folding ? "fold" : "unfold",
17755
+ done: () => {
17756
+ if (morph === entry) morph = null;
17757
+ remove(previous);
17758
+ if (folding) line2.applyOptions(styleOptions(lineStyle2, paint()));
17759
+ }
17760
+ })
17761
+ };
17762
+ morph = entry;
17763
+ }
17630
17764
  events.emit("style", next);
17631
17765
  }
17632
17766
  function content() {
@@ -18038,8 +18172,9 @@ function createChartInstance(deps) {
18038
18172
  countdownClock.destroy();
18039
18173
  pointer?.destroy();
18040
18174
  ranges.stopGlide();
18175
+ morph?.run.finish();
18041
18176
  fling.destroy();
18042
- pinch.destroy();
18177
+ pinch?.destroy();
18043
18178
  freePan?.destroy();
18044
18179
  plotArea.destroy();
18045
18180
  extensions.destroy();
@@ -23133,6 +23268,7 @@ function createChart(options) {
23133
23268
  container: element,
23134
23269
  hostContainer: options.container,
23135
23270
  freePan: options.touch?.freePan === true,
23271
+ styleMorph: options.transitions?.style === true,
23136
23272
  layer,
23137
23273
  datafeed: options.datafeed,
23138
23274
  externalDrawingToolbar: !!options.drawingToolbarContainer,