@trdrs/quickcharts 1.3.0 → 2.0.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.
- package/CHANGELOG.md +94 -0
- package/README.md +209 -200
- package/THIRD-PARTY-NOTICES.md +4 -4
- package/dist/adapters/rest.d.ts +3 -3
- package/dist/adapters/rest.js +2 -2
- package/dist/adapters/rest.js.map +1 -1
- package/dist/{ar-G7MVXRE4.js → ar-KSXQ2USS.js} +20 -20
- package/dist/ar-KSXQ2USS.js.map +1 -0
- package/dist/{assets-ySndlUS3.d.ts → assets-DOuBBsOp.d.ts} +50 -50
- package/dist/{ca_ES-EUSRU4VG.js → ca_ES-JG4FM55E.js} +20 -20
- package/dist/ca_ES-JG4FM55E.js.map +1 -0
- package/dist/{chunk-KFBQSPNA.js → chunk-D3S26ARP.js} +2 -2
- package/dist/chunk-D3S26ARP.js.map +1 -0
- package/dist/{chunk-AKXIFHQN.js → chunk-KLPTCWA2.js} +1 -1
- package/dist/{chunk-AKXIFHQN.js.map → chunk-KLPTCWA2.js.map} +1 -1
- package/dist/{chunk-AA7XKXH4.js → chunk-YR7NT5ZW.js} +67 -33
- package/dist/chunk-YR7NT5ZW.js.map +1 -0
- package/dist/{de-2SVTG25S.js → de-VSW2LHNF.js} +20 -20
- package/dist/de-VSW2LHNF.js.map +1 -0
- package/dist/drawings.d.ts +16 -16
- package/dist/drawings.js +12 -12
- package/dist/{es-UDHZM4W7.js → es-5ZXIPVOB.js} +20 -20
- package/dist/es-5ZXIPVOB.js.map +1 -0
- package/dist/feature-manifest.json +3 -3
- package/dist/{fr-35VN4G4S.js → fr-4F6DAWGD.js} +20 -20
- package/dist/fr-4F6DAWGD.js.map +1 -0
- package/dist/{he_IL-JPZMCURP.js → he_IL-B7PBQUEV.js} +20 -20
- package/dist/he_IL-B7PBQUEV.js.map +1 -0
- package/dist/{id_ID-XSTFHHJQ.js → id_ID-CYJRMXPC.js} +20 -20
- package/dist/id_ID-CYJRMXPC.js.map +1 -0
- package/dist/index.d.ts +167 -163
- package/dist/index.js +342 -268
- package/dist/index.js.map +1 -1
- package/dist/{it-EIY6JESQ.js → it-KTX4SG5P.js} +20 -20
- package/dist/it-KTX4SG5P.js.map +1 -0
- package/dist/{ja-42N4XRCW.js → ja-V7OURU5D.js} +20 -20
- package/dist/ja-V7OURU5D.js.map +1 -0
- package/dist/{ko-3VRPTIP3.js → ko-2RNEBYVE.js} +20 -20
- package/dist/ko-2RNEBYVE.js.map +1 -0
- package/dist/{ms_MY-SKN7O6M5.js → ms_MY-EY235E7Q.js} +20 -20
- package/dist/ms_MY-EY235E7Q.js.map +1 -0
- package/dist/{pl-TYARHCLT.js → pl-ADNVRP55.js} +20 -20
- package/dist/pl-ADNVRP55.js.map +1 -0
- package/dist/{pt-72IIFLL4.js → pt-OL2DR3DO.js} +20 -20
- package/dist/pt-OL2DR3DO.js.map +1 -0
- package/dist/quickcharts.css +97 -96
- package/dist/{resources-Ch8eA1Mw.d.ts → resources-Le6flcEn.d.ts} +3 -3
- package/dist/rest-openapi.json +11 -11
- package/dist/{ru-TPH4F7IB.js → ru-V6YFEPAF.js} +20 -20
- package/dist/ru-V6YFEPAF.js.map +1 -0
- package/dist/{sv-DC5RS67Z.js → sv-TORXQVE7.js} +20 -20
- package/dist/sv-TORXQVE7.js.map +1 -0
- package/dist/{th-I6H6AOZ3.js → th-K6SJSYSY.js} +20 -20
- package/dist/th-K6SJSYSY.js.map +1 -0
- package/dist/theme-manifest.json +10 -10
- package/dist/{tr-7FSUVCNW.js → tr-4AEM66KW.js} +20 -20
- package/dist/tr-4AEM66KW.js.map +1 -0
- package/dist/{vi-DLRPHLLH.js → vi-YU5XCEUU.js} +20 -20
- package/dist/vi-YU5XCEUU.js.map +1 -0
- package/dist/{zh-ZXIQBBEP.js → zh-QXKRYHU5.js} +20 -20
- package/dist/zh-QXKRYHU5.js.map +1 -0
- package/dist/{zh_TW-TYNUJU7U.js → zh_TW-AYPDKG4Q.js} +20 -20
- package/dist/zh_TW-AYPDKG4Q.js.map +1 -0
- package/package.json +2 -2
- package/dist/ar-G7MVXRE4.js.map +0 -1
- package/dist/ca_ES-EUSRU4VG.js.map +0 -1
- package/dist/chunk-AA7XKXH4.js.map +0 -1
- package/dist/chunk-KFBQSPNA.js.map +0 -1
- package/dist/de-2SVTG25S.js.map +0 -1
- package/dist/es-UDHZM4W7.js.map +0 -1
- package/dist/fr-35VN4G4S.js.map +0 -1
- package/dist/he_IL-JPZMCURP.js.map +0 -1
- package/dist/id_ID-XSTFHHJQ.js.map +0 -1
- package/dist/it-EIY6JESQ.js.map +0 -1
- package/dist/ja-42N4XRCW.js.map +0 -1
- package/dist/ko-3VRPTIP3.js.map +0 -1
- package/dist/ms_MY-SKN7O6M5.js.map +0 -1
- package/dist/pl-TYARHCLT.js.map +0 -1
- package/dist/pt-72IIFLL4.js.map +0 -1
- package/dist/ru-TPH4F7IB.js.map +0 -1
- package/dist/sv-DC5RS67Z.js.map +0 -1
- package/dist/th-I6H6AOZ3.js.map +0 -1
- package/dist/tr-7FSUVCNW.js.map +0 -1
- package/dist/vi-DLRPHLLH.js.map +0 -1
- package/dist/zh-ZXIQBBEP.js.map +0 -1
- package/dist/zh_TW-TYNUJU7U.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -31,7 +31,7 @@ import {
|
|
|
31
31
|
TRANSIENT_TOOLS,
|
|
32
32
|
alphaOf,
|
|
33
33
|
blanks,
|
|
34
|
-
|
|
34
|
+
buildDrawingToolbarGroups,
|
|
35
35
|
button,
|
|
36
36
|
cancelText,
|
|
37
37
|
chooseHideMode,
|
|
@@ -44,6 +44,7 @@ import {
|
|
|
44
44
|
cursorButtonArmed,
|
|
45
45
|
dismissOnOutside,
|
|
46
46
|
dragUntilRelease,
|
|
47
|
+
drawingToolbarFaceOf,
|
|
47
48
|
drawingTools,
|
|
48
49
|
editRefused,
|
|
49
50
|
el,
|
|
@@ -65,12 +66,11 @@ import {
|
|
|
65
66
|
paintedPosition,
|
|
66
67
|
parseCssColor,
|
|
67
68
|
parseDrawingPreferences,
|
|
68
|
-
|
|
69
|
+
parseTimeframeContext,
|
|
69
70
|
placePanel,
|
|
70
71
|
pushEscapeOwner,
|
|
71
|
-
railFaceOf,
|
|
72
72
|
readColor,
|
|
73
|
-
|
|
73
|
+
rememberDrawingToolbarTool,
|
|
74
74
|
removableDrawings,
|
|
75
75
|
removeRows,
|
|
76
76
|
restoreDrawings,
|
|
@@ -82,11 +82,11 @@ import {
|
|
|
82
82
|
viewportOf,
|
|
83
83
|
visibilityPreset,
|
|
84
84
|
withAlpha
|
|
85
|
-
} from "./chunk-
|
|
85
|
+
} from "./chunk-YR7NT5ZW.js";
|
|
86
86
|
import {
|
|
87
87
|
ResourceAbortError,
|
|
88
88
|
memorySaveLoadAdapter
|
|
89
|
-
} from "./chunk-
|
|
89
|
+
} from "./chunk-D3S26ARP.js";
|
|
90
90
|
import {
|
|
91
91
|
DRAWING_CONTEXT_VERSION,
|
|
92
92
|
DRAWING_DOCUMENT_VERSION,
|
|
@@ -99,7 +99,7 @@ import {
|
|
|
99
99
|
parseDrawingDocument,
|
|
100
100
|
reviseDrawingDocument,
|
|
101
101
|
sameDrawingContext
|
|
102
|
-
} from "./chunk-
|
|
102
|
+
} from "./chunk-KLPTCWA2.js";
|
|
103
103
|
|
|
104
104
|
// src/datafeed.ts
|
|
105
105
|
function olderPageVerdict(page, askedTo, alreadyHopped) {
|
|
@@ -484,8 +484,7 @@ function createUdfDatafeed(options) {
|
|
|
484
484
|
name: String(col(raw.description, i) ?? symbol),
|
|
485
485
|
exchange: String(col(raw["exchange-listed"], i) ?? group),
|
|
486
486
|
type: String(col(raw.type, i) ?? ""),
|
|
487
|
-
|
|
488
|
-
via: null
|
|
487
|
+
dataSource: null
|
|
489
488
|
});
|
|
490
489
|
}
|
|
491
490
|
}
|
|
@@ -528,8 +527,7 @@ function createUdfDatafeed(options) {
|
|
|
528
527
|
name: r.description ?? r.symbol ?? "",
|
|
529
528
|
exchange: r.exchange ?? "",
|
|
530
529
|
type: r.type ?? "",
|
|
531
|
-
|
|
532
|
-
via: null
|
|
530
|
+
dataSource: null
|
|
533
531
|
}));
|
|
534
532
|
return { hits, hasMore: false };
|
|
535
533
|
},
|
|
@@ -724,8 +722,8 @@ var legend = {
|
|
|
724
722
|
* its percentage. Both arrive already written, sign included. */
|
|
725
723
|
"legend.change": "{change} ({percent}%)",
|
|
726
724
|
"legend.indicatorSettings": "Indicator settings",
|
|
727
|
-
"legend.showRows": "Show
|
|
728
|
-
"legend.hideRows": "Hide
|
|
725
|
+
"legend.showRows": "Show indicator rows",
|
|
726
|
+
"legend.hideRows": "Hide indicator rows",
|
|
729
727
|
"legend.restorePane": "Restore pane",
|
|
730
728
|
"legend.collapsePane": "Collapse pane",
|
|
731
729
|
"legend.maximizePane": "Maximize pane",
|
|
@@ -740,7 +738,7 @@ var legend = {
|
|
|
740
738
|
// The compare surface's legend side: the header door and the compare chips' verbs. The dialog
|
|
741
739
|
// both open belongs to the search family (search.ts).
|
|
742
740
|
"legend.changeSymbol": "Change symbol",
|
|
743
|
-
"legend.removeCompare": "Remove
|
|
741
|
+
"legend.removeCompare": "Remove comparison",
|
|
744
742
|
"legend.removeIndicator": "Remove indicator"
|
|
745
743
|
};
|
|
746
744
|
|
|
@@ -891,14 +889,14 @@ var replay = {
|
|
|
891
889
|
"replay.speed": "Replay speed",
|
|
892
890
|
"replay.updatesPerSecond": { one: "{count} update per second", other: "{count} updates per second" },
|
|
893
891
|
"replay.oneUpdatePerSeconds": { one: "1 update per {count} second", other: "1 update per {count} seconds" },
|
|
894
|
-
// The update
|
|
895
|
-
"replay.
|
|
896
|
-
"replay.
|
|
892
|
+
// The update timeframe.
|
|
893
|
+
"replay.timeframe": "Update timeframe",
|
|
894
|
+
"replay.timeframeHelp": "How much time each replay update advances. Finer than the chart timeframe, each bar forms from real finer bars.",
|
|
897
895
|
/** Why the control is unavailable: this timeframe has nothing finer to form its bars from. */
|
|
898
|
-
"replay.
|
|
899
|
-
/** The
|
|
896
|
+
"replay.timeframeNone": "No finer timeframe for this chart timeframe",
|
|
897
|
+
/** The timeframe option that picks the grain itself, rather than a named token. */
|
|
900
898
|
"replay.auto": "Auto",
|
|
901
|
-
"replay.
|
|
899
|
+
"replay.autoSelectTimeframe": "Auto select timeframe"
|
|
902
900
|
};
|
|
903
901
|
|
|
904
902
|
// src/i18n/en/inputs.ts
|
|
@@ -980,17 +978,17 @@ var layouts = {
|
|
|
980
978
|
"layouts.arrangement": "Chart arrangement",
|
|
981
979
|
"layouts.syncInLayout": "Sync in layout",
|
|
982
980
|
"layouts.syncSymbol": "Symbol",
|
|
983
|
-
"layouts.
|
|
981
|
+
"layouts.syncTimeframe": "Timeframe",
|
|
984
982
|
"layouts.syncCrosshair": "Crosshair",
|
|
985
983
|
"layouts.syncTime": "Time",
|
|
986
984
|
"layouts.syncDateRange": "Date range",
|
|
987
985
|
"layouts.syncSymbolTip": "Symbol changes on all charts within the layout",
|
|
988
|
-
"layouts.
|
|
986
|
+
"layouts.syncTimeframeTip": "Timeframe changes on all charts within the layout",
|
|
989
987
|
"layouts.syncCrosshairTip": "Crosshair is synced across all charts within the layout",
|
|
990
988
|
"layouts.syncTimeTip": "When a chart is clicked, all charts within the layout display the same point of time",
|
|
991
989
|
"layouts.syncDateRangeTip": "Date range changes on all charts within the layout",
|
|
992
990
|
"layouts.syncSymbolToggle": "Sync symbol",
|
|
993
|
-
"layouts.
|
|
991
|
+
"layouts.syncTimeframeToggle": "Sync timeframe",
|
|
994
992
|
"layouts.syncCrosshairToggle": "Sync crosshair",
|
|
995
993
|
"layouts.syncTimeToggle": "Sync time",
|
|
996
994
|
"layouts.syncDateRangeToggle": "Sync date range",
|
|
@@ -1029,9 +1027,9 @@ var layouts = {
|
|
|
1029
1027
|
"layouts.loading": "Loading",
|
|
1030
1028
|
"layouts.emptyNone": "No saved layouts yet.",
|
|
1031
1029
|
"layouts.openLayoutRow": "Open layout\u2026",
|
|
1032
|
-
/** A saved layout's second line: the market and
|
|
1033
|
-
* written as the toolbar writes them (`BTCUSDC`, `1h`). */
|
|
1034
|
-
"layouts.listingFacts": "{symbol}, {
|
|
1030
|
+
/** A saved layout's second line: the market and timeframe its active chart showed at the last
|
|
1031
|
+
* save, written as the toolbar writes them (`BTCUSDC`, `1h`). */
|
|
1032
|
+
"layouts.listingFacts": "{symbol}, {timeframe}",
|
|
1035
1033
|
/** A saved layout's line in the Layouts dialog: what it shows, then when it was last saved. */
|
|
1036
1034
|
"layouts.listingDated": "{facts} ({date})",
|
|
1037
1035
|
// The Layouts dialog.
|
|
@@ -1144,7 +1142,7 @@ var timeframe = {
|
|
|
1144
1142
|
"timeframe.unitDays": "Days",
|
|
1145
1143
|
"timeframe.unitWeeks": "Weeks",
|
|
1146
1144
|
"timeframe.unitMonths": "Months",
|
|
1147
|
-
/** A counted
|
|
1145
|
+
/** A counted timeframe, one plural per unit: '5t' reads "5 Ticks", '1h' reads "1 Hour". */
|
|
1148
1146
|
"timeframe.countTicks": { one: "{count} Tick", other: "{count} Ticks" },
|
|
1149
1147
|
"timeframe.countSeconds": { one: "{count} Second", other: "{count} Seconds" },
|
|
1150
1148
|
"timeframe.countMinutes": { one: "{count} Minute", other: "{count} Minutes" },
|
|
@@ -1152,11 +1150,11 @@ var timeframe = {
|
|
|
1152
1150
|
"timeframe.countDays": { one: "{count} Day", other: "{count} Days" },
|
|
1153
1151
|
"timeframe.countWeeks": { one: "{count} Week", other: "{count} Weeks" },
|
|
1154
1152
|
"timeframe.countMonths": { one: "{count} Month", other: "{count} Months" },
|
|
1155
|
-
/** The per-row verbs; `{timeframe}` is the counted
|
|
1153
|
+
/** The per-row verbs; `{timeframe}` is the counted timeframe. */
|
|
1156
1154
|
"timeframe.delete": "Delete {timeframe}",
|
|
1157
1155
|
"timeframe.save": "Save {timeframe}",
|
|
1158
1156
|
"timeframe.exists": "{timeframe} already exists",
|
|
1159
|
-
// The custom-
|
|
1157
|
+
// The custom-timeframe footer: a count stepper, a unit picker, and Add.
|
|
1160
1158
|
"timeframe.custom": "Custom timeframe",
|
|
1161
1159
|
"timeframe.customCount": "Custom timeframe count",
|
|
1162
1160
|
"timeframe.customUnit": "Custom timeframe unit",
|
|
@@ -1184,7 +1182,7 @@ var range = {
|
|
|
1184
1182
|
"range.fiveYears": "5 Years",
|
|
1185
1183
|
"range.all": "All data",
|
|
1186
1184
|
/** A preset's tooltip: what it frames, and the bars it frames it in. */
|
|
1187
|
-
"range.tip": "{range} \xB7 {
|
|
1185
|
+
"range.tip": "{range} \xB7 {timeframe} bars",
|
|
1188
1186
|
"range.zoomIn": "Zoom in",
|
|
1189
1187
|
"range.zoomOut": "Zoom out",
|
|
1190
1188
|
"range.scrollLeft": "Scroll left",
|
|
@@ -1374,7 +1372,7 @@ var drawing = {
|
|
|
1374
1372
|
"drawing.more": "More",
|
|
1375
1373
|
"drawing.moreActions": "More drawing actions",
|
|
1376
1374
|
"drawing.visualOrder": "Visual order",
|
|
1377
|
-
"drawing.
|
|
1375
|
+
"drawing.visibilityOnTimeframes": "Visibility on timeframes",
|
|
1378
1376
|
"drawing.clone": "Clone",
|
|
1379
1377
|
"drawing.copy": "Copy",
|
|
1380
1378
|
"drawing.modifierControl": "Ctrl",
|
|
@@ -1396,10 +1394,10 @@ var drawing = {
|
|
|
1396
1394
|
"drawing.sendToBack": "Send to back",
|
|
1397
1395
|
"drawing.bringForward": "Bring forward",
|
|
1398
1396
|
"drawing.sendBackward": "Send backward",
|
|
1399
|
-
"drawing.visCurrentAndAbove": "Current
|
|
1400
|
-
"drawing.visCurrentAndBelow": "Current
|
|
1401
|
-
"drawing.visCurrentOnly": "Current
|
|
1402
|
-
"drawing.visAll": "All
|
|
1397
|
+
"drawing.visCurrentAndAbove": "Current timeframe and above",
|
|
1398
|
+
"drawing.visCurrentAndBelow": "Current timeframe and below",
|
|
1399
|
+
"drawing.visCurrentOnly": "Current timeframe only",
|
|
1400
|
+
"drawing.visAll": "All timeframes",
|
|
1403
1401
|
// The template dialogs: naming a new template, and confirming a delete.
|
|
1404
1402
|
"drawing.saveTemplate": "Save drawing template",
|
|
1405
1403
|
"drawing.templateName": "Template name",
|
|
@@ -1559,7 +1557,7 @@ var drawing = {
|
|
|
1559
1557
|
// The Coordinates page: one row per anchor, numbered.
|
|
1560
1558
|
"drawing.coordBar": "#{n} (bar)",
|
|
1561
1559
|
"drawing.coordPriceBar": "#{n} (price, bar)",
|
|
1562
|
-
// The Visibility page: which
|
|
1560
|
+
// The Visibility page: which timeframes paint the drawing.
|
|
1563
1561
|
"drawing.unitTicks": "Ticks",
|
|
1564
1562
|
"drawing.unitSeconds": "Seconds",
|
|
1565
1563
|
"drawing.unitMinutes": "Minutes",
|
|
@@ -1567,7 +1565,7 @@ var drawing = {
|
|
|
1567
1565
|
"drawing.unitDays": "Days",
|
|
1568
1566
|
"drawing.unitWeeks": "Weeks",
|
|
1569
1567
|
"drawing.unitMonths": "Months",
|
|
1570
|
-
"drawing.
|
|
1568
|
+
"drawing.timeframePinnedNote": "A drawing stays on at least one timeframe, so it can always be reached again.",
|
|
1571
1569
|
// The color palette: swatches, the custom color panel, the opacity row.
|
|
1572
1570
|
"drawing.colorSwatch": "Color {hex}",
|
|
1573
1571
|
"drawing.customColor": "Custom color",
|
|
@@ -1651,7 +1649,7 @@ var command = {
|
|
|
1651
1649
|
"command.indicatorShow": "Show indicator",
|
|
1652
1650
|
"command.drawingsRemoveAll": "Remove drawings",
|
|
1653
1651
|
"command.drawingDeleteSelected": "Delete selected drawing",
|
|
1654
|
-
/** Arm a drawing tool. The tool is the argument, so the
|
|
1652
|
+
/** Arm a drawing tool. The tool is the argument, so the 90 tools share one command. */
|
|
1655
1653
|
"command.drawingArm": "Drawing tool",
|
|
1656
1654
|
// The toolbar's switches and menus. Each takes its value as the argument.
|
|
1657
1655
|
"command.drawingCursor": "Cursor mode",
|
|
@@ -1675,7 +1673,7 @@ var command = {
|
|
|
1675
1673
|
"command.drawingBringForward": "Bring drawing forward",
|
|
1676
1674
|
"command.drawingSendBackward": "Send drawing backward",
|
|
1677
1675
|
"command.drawingHideSelected": "Hide drawing",
|
|
1678
|
-
"command.drawingVisibility": "Drawing visibility on
|
|
1676
|
+
"command.drawingVisibility": "Drawing visibility on timeframes",
|
|
1679
1677
|
"command.drawingSettings": "Drawing settings",
|
|
1680
1678
|
"command.drawingCommitEdit": "Apply drawing settings",
|
|
1681
1679
|
"command.drawingPlaceImage": "Place image",
|
|
@@ -1700,7 +1698,7 @@ var command = {
|
|
|
1700
1698
|
"command.replayStepBack": "Step back",
|
|
1701
1699
|
"command.replayGoLive": "Replay to the live edge",
|
|
1702
1700
|
"command.replaySpeed": "Replay speed",
|
|
1703
|
-
"command.
|
|
1701
|
+
"command.replayTimeframe": "Replay update timeframe",
|
|
1704
1702
|
"command.timeframeSet": "Timeframe",
|
|
1705
1703
|
/** Which subsession intraday bars are shown for. A symbol with no extended hours offers only
|
|
1706
1704
|
* the first. */
|
|
@@ -1893,26 +1891,26 @@ function readingDirection(i18n) {
|
|
|
1893
1891
|
return BUILT_IN_LOCALES.find((l) => l.code === i18n.locale())?.dir ?? "ltr";
|
|
1894
1892
|
}
|
|
1895
1893
|
var chartDictionaries = createDictionaryLoader(en, {
|
|
1896
|
-
de: () => import("./de-
|
|
1897
|
-
fr: () => import("./fr-
|
|
1898
|
-
es: () => import("./es-
|
|
1899
|
-
it: () => import("./it-
|
|
1900
|
-
ca_ES: () => import("./ca_ES-
|
|
1901
|
-
pl: () => import("./pl-
|
|
1902
|
-
sv: () => import("./sv-
|
|
1903
|
-
tr: () => import("./tr-
|
|
1904
|
-
ru: () => import("./ru-
|
|
1905
|
-
pt: () => import("./pt-
|
|
1906
|
-
id_ID: () => import("./id_ID-
|
|
1907
|
-
ms_MY: () => import("./ms_MY-
|
|
1908
|
-
th: () => import("./th-
|
|
1909
|
-
vi: () => import("./vi-
|
|
1910
|
-
ja: () => import("./ja-
|
|
1911
|
-
ko: () => import("./ko-
|
|
1912
|
-
zh: () => import("./zh-
|
|
1913
|
-
zh_TW: () => import("./zh_TW-
|
|
1914
|
-
ar: () => import("./ar-
|
|
1915
|
-
he_IL: () => import("./he_IL-
|
|
1894
|
+
de: () => import("./de-VSW2LHNF.js"),
|
|
1895
|
+
fr: () => import("./fr-4F6DAWGD.js"),
|
|
1896
|
+
es: () => import("./es-5ZXIPVOB.js"),
|
|
1897
|
+
it: () => import("./it-KTX4SG5P.js"),
|
|
1898
|
+
ca_ES: () => import("./ca_ES-JG4FM55E.js"),
|
|
1899
|
+
pl: () => import("./pl-ADNVRP55.js"),
|
|
1900
|
+
sv: () => import("./sv-TORXQVE7.js"),
|
|
1901
|
+
tr: () => import("./tr-4AEM66KW.js"),
|
|
1902
|
+
ru: () => import("./ru-V6YFEPAF.js"),
|
|
1903
|
+
pt: () => import("./pt-OL2DR3DO.js"),
|
|
1904
|
+
id_ID: () => import("./id_ID-CYJRMXPC.js"),
|
|
1905
|
+
ms_MY: () => import("./ms_MY-EY235E7Q.js"),
|
|
1906
|
+
th: () => import("./th-K6SJSYSY.js"),
|
|
1907
|
+
vi: () => import("./vi-YU5XCEUU.js"),
|
|
1908
|
+
ja: () => import("./ja-V7OURU5D.js"),
|
|
1909
|
+
ko: () => import("./ko-2RNEBYVE.js"),
|
|
1910
|
+
zh: () => import("./zh-QXKRYHU5.js"),
|
|
1911
|
+
zh_TW: () => import("./zh_TW-AYPDKG4Q.js"),
|
|
1912
|
+
ar: () => import("./ar-KSXQ2USS.js"),
|
|
1913
|
+
he_IL: () => import("./he_IL-B7PBQUEV.js")
|
|
1916
1914
|
});
|
|
1917
1915
|
var dynamic = (t) => t;
|
|
1918
1916
|
var source = null;
|
|
@@ -2180,9 +2178,9 @@ var THEME_ROLES = [
|
|
|
2180
2178
|
{ id: "canvas.background", family: "canvas", kind: "color", description: "The fill behind the plotted series." },
|
|
2181
2179
|
{ id: "canvas.paneBorder", family: "canvas", kind: "color", description: "The divider between stacked panes and around the plot area." },
|
|
2182
2180
|
// ── series: direction, before any chart appearance override ─────────────────────────────────
|
|
2183
|
-
{ id: "series.up", family: "series", kind: "color", description: "A rising value: up volume, a rising
|
|
2184
|
-
{ id: "series.down", family: "series", kind: "color", description: "A falling value: down volume, a falling
|
|
2185
|
-
{ id: "series.neutral", family: "series", kind: "color", description: "A series or level with no direction, such as
|
|
2181
|
+
{ id: "series.up", family: "series", kind: "color", description: "A rising value: up volume, a rising indicator tint, a positive series default." },
|
|
2182
|
+
{ id: "series.down", family: "series", kind: "color", description: "A falling value: down volume, a falling indicator tint, a negative series default." },
|
|
2183
|
+
{ id: "series.neutral", family: "series", kind: "color", description: "A series or level with no direction, such as an indicator level line." },
|
|
2186
2184
|
// ── scale: grid, axes, crosshair, and session shading ───────────────────────────────────────
|
|
2187
2185
|
{ id: "scale.grid", family: "scale", kind: "color", description: "The grid lines drawn across the plot area." },
|
|
2188
2186
|
{ id: "scale.background", family: "scale", kind: "color", description: "The ground behind the price and time scales." },
|
|
@@ -2257,7 +2255,7 @@ var THEME_ROLES = [
|
|
|
2257
2255
|
{ id: "text.fontSizeSmall", family: "text", kind: "length", description: "Size of dense control text, such as a legend row." },
|
|
2258
2256
|
{ id: "text.fontSizeMicro", family: "text", kind: "length", description: "Size of the smallest chip and hint text." },
|
|
2259
2257
|
// ── chrome: the surfaces and borders of the chart's own controls ────────────────────────────
|
|
2260
|
-
{ id: "chrome.surface", family: "chrome", kind: "color", description: "The fill of toolbars,
|
|
2258
|
+
{ id: "chrome.surface", family: "chrome", kind: "color", description: "The fill of toolbars, bars, and panels." },
|
|
2261
2259
|
{ id: "chrome.surfaceRaised", family: "chrome", kind: "color", description: "The fill of a control sitting on a chrome surface, such as a button or field." },
|
|
2262
2260
|
{ id: "chrome.border", family: "chrome", kind: "color", description: "The ordinary border of a control or panel." },
|
|
2263
2261
|
{ id: "chrome.borderStrong", family: "chrome", kind: "color", description: "A border or divider that must stay visible at a glance.", contrast: [{ over: "chrome.surface", min: 3 }] },
|
|
@@ -2359,7 +2357,7 @@ var THEME_ROLES = [
|
|
|
2359
2357
|
{ id: "illustration.ink", family: "illustration", kind: "color", description: "The line drawing of an empty-state illustration.", contrast: [{ over: "overlay.surface", min: 3 }] },
|
|
2360
2358
|
{ id: "illustration.accent", family: "illustration", kind: "color", description: "The badge an empty-state illustration wears on the action it invites.", contrast: [{ over: "overlay.surface", min: 3 }] },
|
|
2361
2359
|
{ id: "illustration.accentInk", family: "illustration", kind: "color", description: "The mark on an illustration's badge.", contrast: [{ over: "illustration.accent", min: 3 }] },
|
|
2362
|
-
// ── drawing: what a new drawing wears before a
|
|
2360
|
+
// ── drawing: what a new drawing wears before a viewer styles it ─────────────────────────────
|
|
2363
2361
|
{ id: "drawing.line", family: "drawing", kind: "color", description: "The stroke of a newly placed drawing." },
|
|
2364
2362
|
{ id: "drawing.fill", family: "drawing", kind: "color", description: "The fill of a newly placed drawing that has an area." },
|
|
2365
2363
|
{ id: "drawing.text", family: "drawing", kind: "color", description: "The ink of a drawing label." },
|
|
@@ -2372,7 +2370,7 @@ var THEME_ROLES = [
|
|
|
2372
2370
|
{ id: "motion.durationBase", family: "motion", kind: "duration", description: "An ordinary transition: a modal dialog and its backdrop opening and closing, and the replay row entering and leaving." },
|
|
2373
2371
|
{ id: "motion.durationModerate", family: "motion", kind: "duration", description: "A control moving to its new state, such as a switch knob sliding." },
|
|
2374
2372
|
{ id: "motion.durationSlow", family: "motion", kind: "duration", description: "A small mark settling, such as a disclosure caret turning or a checkbox filling." },
|
|
2375
|
-
{ id: "motion.durationSlower", family: "motion", kind: "duration", description: "A larger mark turning, such as the drawing
|
|
2373
|
+
{ id: "motion.durationSlower", family: "motion", kind: "duration", description: "A larger mark turning, such as the drawing toolbar's chevron." },
|
|
2376
2374
|
{ id: "motion.easingStandard", family: "motion", kind: "easing", description: "The timing of an ordinary transition: a modal dialog box, the replay row, a caret dip, a checkbox fill." },
|
|
2377
2375
|
{ id: "motion.easingOut", family: "motion", kind: "easing", description: "The timing of a motion that settles as it ends: a modal backdrop fading, a switch knob sliding." },
|
|
2378
2376
|
{ id: "motion.easingLinear", family: "motion", kind: "easing", description: "The timing of a color or opacity change under the pointer." },
|
|
@@ -2521,9 +2519,9 @@ function cssVarName(roleId) {
|
|
|
2521
2519
|
function themeDeclarations(theme) {
|
|
2522
2520
|
return Object.keys(theme).sort().map((roleId) => ({ property: cssVarName(roleId), value: String(theme[roleId]) }));
|
|
2523
2521
|
}
|
|
2524
|
-
var STYLE_LAYERS = { tokens: "
|
|
2522
|
+
var STYLE_LAYERS = { tokens: "quickcharts.tokens", chart: "quickcharts.chart" };
|
|
2525
2523
|
var LAYER_ORDER_STATEMENT = `@layer ${STYLE_LAYERS.tokens}, ${STYLE_LAYERS.chart};`;
|
|
2526
|
-
var HOST_LAYER_ORDER = `@layer reset, ${STYLE_LAYERS.tokens}, ${STYLE_LAYERS.chart},
|
|
2524
|
+
var HOST_LAYER_ORDER = `@layer reset, ${STYLE_LAYERS.tokens}, ${STYLE_LAYERS.chart}, host;`;
|
|
2527
2525
|
|
|
2528
2526
|
// src/theme/renderer.ts
|
|
2529
2527
|
function themeRootStyle(mode, theme) {
|
|
@@ -4491,7 +4489,7 @@ function rangeAvailable(preset, earliestBarSecs, nowSecs = Math.floor(Date.now()
|
|
|
4491
4489
|
return nowSecs - earliestBarSecs >= preset.span;
|
|
4492
4490
|
}
|
|
4493
4491
|
function rangePresetTip(t, preset) {
|
|
4494
|
-
return t("range.tip", { range: t(preset.label),
|
|
4492
|
+
return t("range.tip", { range: t(preset.label), timeframe: timeframeLabel(t, preset.tf) });
|
|
4495
4493
|
}
|
|
4496
4494
|
function frameRange(chart, series2, span, tf, nowSecs = Math.floor(Date.now() / 1e3)) {
|
|
4497
4495
|
const data = series2.data();
|
|
@@ -4892,7 +4890,7 @@ var LAYOUT_MENU_ROWS = [
|
|
|
4892
4890
|
];
|
|
4893
4891
|
|
|
4894
4892
|
// src/widget/arrangements.ts
|
|
4895
|
-
var LAYOUT_SYNC_KEYS = ["symbol", "
|
|
4893
|
+
var LAYOUT_SYNC_KEYS = ["symbol", "timeframe", "crosshair", "time", "dateRange"];
|
|
4896
4894
|
var ALL_LAYOUTS_OFFERED = { arrangements: ARRANGEMENTS.map((a) => a.code), named: false, sync: LAYOUT_SYNC_KEYS };
|
|
4897
4895
|
var SYNC_PHRASE = `it takes ${LAYOUT_SYNC_KEYS.join(", ")}`;
|
|
4898
4896
|
function resolveOfferedLayouts(layouts2, layoutSync, arrangement) {
|
|
@@ -4962,9 +4960,9 @@ var DEFAULT_OVERRIDES = {
|
|
|
4962
4960
|
background: "#ece7c0",
|
|
4963
4961
|
upColor: "#26a69a",
|
|
4964
4962
|
downColor: "#ffa726",
|
|
4965
|
-
// Candle anatomy: solid black ring and wick, the SAME ink both directions
|
|
4966
|
-
//
|
|
4967
|
-
//
|
|
4963
|
+
// Candle anatomy: solid black ring and wick, the SAME ink both directions. On a paper canvas
|
|
4964
|
+
// the black is what separates a candle from the background, and that job does not change with
|
|
4965
|
+
// direction.
|
|
4968
4966
|
borderUpColor: "#000000",
|
|
4969
4967
|
borderDownColor: "#000000",
|
|
4970
4968
|
wickUpColor: "#000000",
|
|
@@ -8134,6 +8132,10 @@ var PLACE_DRAG_PX = 6;
|
|
|
8134
8132
|
var MOVE_EPSILON_PX = 2;
|
|
8135
8133
|
var HANDLE_GRAB_PX = 11;
|
|
8136
8134
|
var GRIP_GRAB_PX = 9;
|
|
8135
|
+
var TOUCH_REACH_PX = 14;
|
|
8136
|
+
var TOUCH_GRAB_PX = 22;
|
|
8137
|
+
var TOUCH_PLACE_DRAG_PX = 12;
|
|
8138
|
+
var TOUCH_MOVE_PX = 6;
|
|
8137
8139
|
var STROKE_STEP_PX = 3;
|
|
8138
8140
|
var isTransientArmed = (tool) => tool === "measure" || tool === "zoom" || tool === "eraser";
|
|
8139
8141
|
function bindGestures(ctx) {
|
|
@@ -8165,11 +8167,38 @@ function bindGestures(ctx) {
|
|
|
8165
8167
|
if (mode === "off") return raw;
|
|
8166
8168
|
return magnetSnap(chart, series2, p, mode) ?? raw;
|
|
8167
8169
|
};
|
|
8170
|
+
let touching = false;
|
|
8171
|
+
const hitAt = (p) => {
|
|
8172
|
+
const under = manager.hitTest(p);
|
|
8173
|
+
if (under || !touching) return under;
|
|
8174
|
+
for (const radius of [TOUCH_REACH_PX / 2, TOUCH_REACH_PX]) {
|
|
8175
|
+
for (let k = 0; k < 8; k++) {
|
|
8176
|
+
const angle = k * Math.PI / 4;
|
|
8177
|
+
const near2 = manager.hitTest({ x: p.x + radius * Math.cos(angle), y: p.y + radius * Math.sin(angle) });
|
|
8178
|
+
if (near2) return near2;
|
|
8179
|
+
}
|
|
8180
|
+
}
|
|
8181
|
+
return null;
|
|
8182
|
+
};
|
|
8183
|
+
const showPoint = (anchor) => {
|
|
8184
|
+
if (!touching || !anchor) return;
|
|
8185
|
+
try {
|
|
8186
|
+
chart.setCrosshairPosition(anchor.price, anchor.time, series2);
|
|
8187
|
+
} catch {
|
|
8188
|
+
}
|
|
8189
|
+
};
|
|
8190
|
+
const hidePoint = () => {
|
|
8191
|
+
if (!touching) return;
|
|
8192
|
+
try {
|
|
8193
|
+
chart.clearCrosshairPosition();
|
|
8194
|
+
} catch {
|
|
8195
|
+
}
|
|
8196
|
+
};
|
|
8168
8197
|
const anchorHit = (drawing2, p) => {
|
|
8169
8198
|
const vp = viewport();
|
|
8170
8199
|
if (!vp) return null;
|
|
8171
8200
|
let best = null;
|
|
8172
|
-
let bestD = HANDLE_GRAB_PX;
|
|
8201
|
+
let bestD = touching ? TOUCH_GRAB_PX : HANDLE_GRAB_PX;
|
|
8173
8202
|
for (const cp of drawing2.getControlPoints(vp)) {
|
|
8174
8203
|
const d = Math.hypot(cp.x - p.x, cp.y - p.y);
|
|
8175
8204
|
if (d <= bestD) {
|
|
@@ -8241,6 +8270,7 @@ function bindGestures(ctx) {
|
|
|
8241
8270
|
};
|
|
8242
8271
|
const onDown = (e) => {
|
|
8243
8272
|
if (e.button !== 0) return;
|
|
8273
|
+
touching = e.pointerType === "touch";
|
|
8244
8274
|
if (ctx.textEditOpen()) return;
|
|
8245
8275
|
wearCursor(ctx.cursorCss());
|
|
8246
8276
|
container.focus({ preventScroll: true });
|
|
@@ -8249,7 +8279,7 @@ function bindGestures(ctx) {
|
|
|
8249
8279
|
if (!ctx.draft && ctx.transient.size && tool !== "measure" && tool !== "zoom") ctx.clearTransients();
|
|
8250
8280
|
if (tool === "eraser") {
|
|
8251
8281
|
if (ctx.locked()) return;
|
|
8252
|
-
const hit =
|
|
8282
|
+
const hit = hitAt(p);
|
|
8253
8283
|
if (hit && !editRefused("erase", hit.options, false)) {
|
|
8254
8284
|
manager.remove(hit.id);
|
|
8255
8285
|
ctx.persist();
|
|
@@ -8286,7 +8316,8 @@ function bindGestures(ctx) {
|
|
|
8286
8316
|
}
|
|
8287
8317
|
const vp = viewport();
|
|
8288
8318
|
if (vp) {
|
|
8289
|
-
const
|
|
8319
|
+
const gripReach = touching ? TOUCH_GRAB_PX : GRIP_GRAB_PX;
|
|
8320
|
+
const grip = sel.resizeHandles(vp).findIndex((g) => Math.hypot(g.x - p.x, g.y - p.y) <= gripReach);
|
|
8290
8321
|
if (grip !== -1) {
|
|
8291
8322
|
startDrag("resize", sel, grip, p);
|
|
8292
8323
|
return;
|
|
@@ -8304,7 +8335,7 @@ function bindGestures(ctx) {
|
|
|
8304
8335
|
return;
|
|
8305
8336
|
}
|
|
8306
8337
|
}
|
|
8307
|
-
const hit =
|
|
8338
|
+
const hit = hitAt(p);
|
|
8308
8339
|
if (hit) {
|
|
8309
8340
|
if ((e.ctrlKey || e.metaKey) && !editRefused("cloneDrag", hit.options, false) && ctx.copies(hit.type)) {
|
|
8310
8341
|
const copy = drawingTools.restore({ ...hit.toJSON(), id: ctx.nextId() });
|
|
@@ -8324,7 +8355,10 @@ function bindGestures(ctx) {
|
|
|
8324
8355
|
manager.deselect();
|
|
8325
8356
|
return;
|
|
8326
8357
|
}
|
|
8327
|
-
if (ctx.draft)
|
|
8358
|
+
if (ctx.draft) {
|
|
8359
|
+
if (touching) followDraft(ctx.draft, p, e.shiftKey);
|
|
8360
|
+
return;
|
|
8361
|
+
}
|
|
8328
8362
|
if (ctx.locked()) return;
|
|
8329
8363
|
const def = drawingTools.get(tool);
|
|
8330
8364
|
if (!def) return;
|
|
@@ -8357,6 +8391,7 @@ function bindGestures(ctx) {
|
|
|
8357
8391
|
}
|
|
8358
8392
|
manager.add(drawing2);
|
|
8359
8393
|
ctx.draft = { drawing: drawing2, required, placed: 1, downX: p.x, downY: p.y, pendingDrag: true, hasText: !!def.hasText, mode, lastX: p.x, lastY: p.y };
|
|
8394
|
+
showPoint(anchor);
|
|
8360
8395
|
if (mode === "fixed" && required === 1) completePlacement(ctx.draft, p);
|
|
8361
8396
|
};
|
|
8362
8397
|
const constrainedEnd = (draft, p, shift) => {
|
|
@@ -8365,12 +8400,22 @@ function bindGestures(ctx) {
|
|
|
8365
8400
|
const first = vp && draft.drawing.anchors[0] ? draft.drawing.anchorToPixel(draft.drawing.anchors[0], vp) : null;
|
|
8366
8401
|
return first ? constrain45(first, p) : p;
|
|
8367
8402
|
};
|
|
8403
|
+
const followDraft = (draft, p, shift) => {
|
|
8404
|
+
const live = constrainedEnd(draft, p, shift);
|
|
8405
|
+
const anchor = isTransientArmed(ctx.armed()) ? anchorAt(live) : snappedAnchorAt(live, shift);
|
|
8406
|
+
if (anchor) draft.drawing.updateAnchor(draft.drawing.anchors.length - 1, anchor);
|
|
8407
|
+
showPoint(anchor);
|
|
8408
|
+
};
|
|
8368
8409
|
const onMove = (e) => {
|
|
8369
8410
|
const p = localXY(e);
|
|
8370
8411
|
const drag = ctx.drag;
|
|
8371
8412
|
if (drag) {
|
|
8372
8413
|
let { x, y } = p;
|
|
8373
|
-
if (!drag.moved
|
|
8414
|
+
if (!drag.moved) {
|
|
8415
|
+
const travel = Math.abs(x - drag.grabX) + Math.abs(y - drag.grabY);
|
|
8416
|
+
if (touching && travel <= TOUCH_MOVE_PX) return;
|
|
8417
|
+
if (travel > MOVE_EPSILON_PX) drag.moved = true;
|
|
8418
|
+
}
|
|
8374
8419
|
const vp = viewport();
|
|
8375
8420
|
if (!vp) return;
|
|
8376
8421
|
if (drag.mode === "resize" && drag.anchorIndex !== null) {
|
|
@@ -8384,6 +8429,7 @@ function bindGestures(ctx) {
|
|
|
8384
8429
|
}
|
|
8385
8430
|
const anchor = snappedAnchorAt({ x, y }, e.shiftKey);
|
|
8386
8431
|
if (anchor) drag.drawing.updateAnchor(drag.anchorIndex, anchor);
|
|
8432
|
+
showPoint(anchor);
|
|
8387
8433
|
return;
|
|
8388
8434
|
}
|
|
8389
8435
|
const dx = x - drag.grabX;
|
|
@@ -8406,18 +8452,16 @@ function bindGestures(ctx) {
|
|
|
8406
8452
|
const draft = ctx.draft;
|
|
8407
8453
|
if (draft) {
|
|
8408
8454
|
if (draft.mode === "freehand") {
|
|
8409
|
-
const
|
|
8410
|
-
if (!
|
|
8455
|
+
const anchor = anchorAt(p);
|
|
8456
|
+
if (!anchor) return;
|
|
8411
8457
|
if (Math.hypot(p.x - draft.lastX, p.y - draft.lastY) >= STROKE_STEP_PX) {
|
|
8412
|
-
draft.drawing.appendAnchor(
|
|
8458
|
+
draft.drawing.appendAnchor(anchor);
|
|
8413
8459
|
draft.lastX = p.x;
|
|
8414
8460
|
draft.lastY = p.y;
|
|
8415
8461
|
}
|
|
8416
8462
|
return;
|
|
8417
8463
|
}
|
|
8418
|
-
|
|
8419
|
-
const anchor = isTransientArmed(ctx.armed()) ? anchorAt(live) : snappedAnchorAt(live, e.shiftKey);
|
|
8420
|
-
if (anchor) draft.drawing.updateAnchor(draft.drawing.anchors.length - 1, anchor);
|
|
8464
|
+
followDraft(draft, p, e.shiftKey);
|
|
8421
8465
|
return;
|
|
8422
8466
|
}
|
|
8423
8467
|
wearCursor(ctx.cursorCss());
|
|
@@ -8427,33 +8471,38 @@ function bindGestures(ctx) {
|
|
|
8427
8471
|
ctx.setHovered(manager.hitTest(localXY(e))?.id ?? null);
|
|
8428
8472
|
};
|
|
8429
8473
|
const onLeave = () => ctx.setHovered(null);
|
|
8474
|
+
const placeSlop = () => touching ? TOUCH_PLACE_DRAG_PX : PLACE_DRAG_PX;
|
|
8475
|
+
const endDrag = (drag) => {
|
|
8476
|
+
ctx.drag = null;
|
|
8477
|
+
freezePan(!!ctx.armed());
|
|
8478
|
+
if (drag.cloned && !drag.moved) manager.remove(drag.drawing.id);
|
|
8479
|
+
if (!drag.moved && !drag.cloned && drag.mode === "move" && drag.drawing.type === "table") {
|
|
8480
|
+
const vp = viewport();
|
|
8481
|
+
const table = drag.drawing;
|
|
8482
|
+
const cell2 = vp && table.cellAt ? table.cellAt({ x: drag.grabX, y: drag.grabY }, vp) : null;
|
|
8483
|
+
if (cell2) {
|
|
8484
|
+
ctx.openCellEdit(drag.drawing, cell2);
|
|
8485
|
+
return;
|
|
8486
|
+
}
|
|
8487
|
+
}
|
|
8488
|
+
if (drag.moved || drag.cloned) {
|
|
8489
|
+
ctx.persist();
|
|
8490
|
+
ctx.changed();
|
|
8491
|
+
}
|
|
8492
|
+
};
|
|
8430
8493
|
const onUp = (e) => {
|
|
8431
8494
|
const p = localXY(e);
|
|
8495
|
+
hidePoint();
|
|
8432
8496
|
const drag = ctx.drag;
|
|
8433
8497
|
if (drag) {
|
|
8434
|
-
|
|
8435
|
-
freezePan(!!ctx.armed());
|
|
8436
|
-
if (drag.cloned && !drag.moved) manager.remove(drag.drawing.id);
|
|
8437
|
-
if (!drag.moved && !drag.cloned && drag.mode === "move" && drag.drawing.type === "table") {
|
|
8438
|
-
const vp = viewport();
|
|
8439
|
-
const table = drag.drawing;
|
|
8440
|
-
const cell2 = vp && table.cellAt ? table.cellAt({ x: drag.grabX, y: drag.grabY }, vp) : null;
|
|
8441
|
-
if (cell2) {
|
|
8442
|
-
ctx.openCellEdit(drag.drawing, cell2);
|
|
8443
|
-
return;
|
|
8444
|
-
}
|
|
8445
|
-
}
|
|
8446
|
-
if (drag.moved || drag.cloned) {
|
|
8447
|
-
ctx.persist();
|
|
8448
|
-
ctx.changed();
|
|
8449
|
-
}
|
|
8498
|
+
endDrag(drag);
|
|
8450
8499
|
return;
|
|
8451
8500
|
}
|
|
8452
8501
|
const draft = ctx.draft;
|
|
8453
8502
|
if (!draft) return;
|
|
8454
8503
|
const tool = ctx.armed();
|
|
8455
8504
|
if (tool === "measure" || tool === "zoom") {
|
|
8456
|
-
const moved2 = Math.abs(p.x - draft.downX) + Math.abs(p.y - draft.downY) > 4;
|
|
8505
|
+
const moved2 = Math.abs(p.x - draft.downX) + Math.abs(p.y - draft.downY) > (touching ? TOUCH_PLACE_DRAG_PX : 4);
|
|
8457
8506
|
if (!moved2 && draft.pendingDrag) {
|
|
8458
8507
|
draft.pendingDrag = false;
|
|
8459
8508
|
return;
|
|
@@ -8473,7 +8522,7 @@ function bindGestures(ctx) {
|
|
|
8473
8522
|
return;
|
|
8474
8523
|
}
|
|
8475
8524
|
if (draft.mode === "multipoint") {
|
|
8476
|
-
if (draft.pendingDrag && Math.abs(p.x - draft.downX) + Math.abs(p.y - draft.downY) <=
|
|
8525
|
+
if (draft.pendingDrag && Math.abs(p.x - draft.downX) + Math.abs(p.y - draft.downY) <= placeSlop()) {
|
|
8477
8526
|
draft.pendingDrag = false;
|
|
8478
8527
|
return;
|
|
8479
8528
|
}
|
|
@@ -8489,7 +8538,7 @@ function bindGestures(ctx) {
|
|
|
8489
8538
|
const snapped = snappedAnchorAt(constrainedEnd(draft, p, e.shiftKey), e.shiftKey);
|
|
8490
8539
|
const lastIndex = draft.drawing.anchors.length - 1;
|
|
8491
8540
|
if (snapped) draft.drawing.updateAnchor(lastIndex, snapped);
|
|
8492
|
-
const moved = Math.abs(p.x - draft.downX) + Math.abs(p.y - draft.downY) >
|
|
8541
|
+
const moved = Math.abs(p.x - draft.downX) + Math.abs(p.y - draft.downY) > placeSlop();
|
|
8493
8542
|
if (draft.pendingDrag && !moved) {
|
|
8494
8543
|
draft.pendingDrag = false;
|
|
8495
8544
|
return;
|
|
@@ -8503,6 +8552,14 @@ function bindGestures(ctx) {
|
|
|
8503
8552
|
draft.drawing.appendAnchor({ ...next });
|
|
8504
8553
|
}
|
|
8505
8554
|
};
|
|
8555
|
+
const onCancel = () => {
|
|
8556
|
+
hidePoint();
|
|
8557
|
+
if (ctx.drag) endDrag(ctx.drag);
|
|
8558
|
+
else if (ctx.draft) ctx.draft.pendingDrag = false;
|
|
8559
|
+
};
|
|
8560
|
+
const onTouchStart = (e) => {
|
|
8561
|
+
if (ctx.drag || ctx.draft) e.preventDefault();
|
|
8562
|
+
};
|
|
8506
8563
|
const onDblClick = (e) => {
|
|
8507
8564
|
if (ctx.locked()) return;
|
|
8508
8565
|
const draft = ctx.draft;
|
|
@@ -8527,18 +8584,22 @@ function bindGestures(ctx) {
|
|
|
8527
8584
|
ctx.openTextEdit(hit, p.x, p.y, false);
|
|
8528
8585
|
};
|
|
8529
8586
|
container.addEventListener("pointerdown", onDown);
|
|
8587
|
+
container.addEventListener("touchstart", onTouchStart, { passive: false });
|
|
8530
8588
|
container.addEventListener("pointermove", onHover);
|
|
8531
8589
|
container.addEventListener("pointerleave", onLeave);
|
|
8532
8590
|
container.addEventListener("dblclick", onDblClick);
|
|
8533
8591
|
window.addEventListener("pointermove", onMove);
|
|
8534
8592
|
window.addEventListener("pointerup", onUp);
|
|
8593
|
+
window.addEventListener("pointercancel", onCancel);
|
|
8535
8594
|
return () => {
|
|
8536
8595
|
container.removeEventListener("pointerdown", onDown);
|
|
8596
|
+
container.removeEventListener("touchstart", onTouchStart);
|
|
8537
8597
|
container.removeEventListener("pointermove", onHover);
|
|
8538
8598
|
container.removeEventListener("pointerleave", onLeave);
|
|
8539
8599
|
container.removeEventListener("dblclick", onDblClick);
|
|
8540
8600
|
window.removeEventListener("pointermove", onMove);
|
|
8541
8601
|
window.removeEventListener("pointerup", onUp);
|
|
8602
|
+
window.removeEventListener("pointercancel", onCancel);
|
|
8542
8603
|
};
|
|
8543
8604
|
}
|
|
8544
8605
|
|
|
@@ -8596,7 +8657,7 @@ function attachDrawings(options) {
|
|
|
8596
8657
|
const manager = new DrawingManager();
|
|
8597
8658
|
manager.setGlyphSource(options.glyphSource ?? bundledGlyphSource);
|
|
8598
8659
|
manager.attach(chart, series2);
|
|
8599
|
-
manager.
|
|
8660
|
+
manager.setTimeframeContext(parseTimeframeContext(options.timeframe ?? ""));
|
|
8600
8661
|
if (options.bars) {
|
|
8601
8662
|
const read = options.bars;
|
|
8602
8663
|
let cacheKey = "";
|
|
@@ -8974,7 +9035,7 @@ function attachDrawings(options) {
|
|
|
8974
9035
|
setVisibilityPreset(preset) {
|
|
8975
9036
|
const sel = selection();
|
|
8976
9037
|
if (!sel) return;
|
|
8977
|
-
sel.updateOptions({ visibility: visibilityPreset(preset,
|
|
9038
|
+
sel.updateOptions({ visibility: visibilityPreset(preset, parseTimeframeContext(timeframe2)) });
|
|
8978
9039
|
persist();
|
|
8979
9040
|
changed();
|
|
8980
9041
|
},
|
|
@@ -9041,7 +9102,7 @@ function attachDrawings(options) {
|
|
|
9041
9102
|
},
|
|
9042
9103
|
setTimeframe(tf) {
|
|
9043
9104
|
timeframe2 = tf;
|
|
9044
|
-
manager.
|
|
9105
|
+
manager.setTimeframeContext(parseTimeframeContext(tf));
|
|
9045
9106
|
},
|
|
9046
9107
|
setTick: (tick) => manager.setTickSize(tick),
|
|
9047
9108
|
setPriceFormatter: (format2) => manager.setPriceFormatter(format2),
|
|
@@ -9776,17 +9837,17 @@ var HIDE_ICON = {
|
|
|
9776
9837
|
};
|
|
9777
9838
|
function mountDrawingToolbar(deps) {
|
|
9778
9839
|
const { t } = deps;
|
|
9779
|
-
const groups =
|
|
9840
|
+
const groups = buildDrawingToolbarGroups();
|
|
9780
9841
|
const shown2 = (command2) => deps.shown?.(command2) ?? true;
|
|
9781
9842
|
const toolShown = (type) => deps.toolShown?.(type) ?? true;
|
|
9782
9843
|
const drawnGroup = (group) => ({
|
|
9783
9844
|
...group,
|
|
9784
9845
|
sections: group.sections.map((section) => ({ ...section, tools: section.tools.filter((tool) => toolShown(tool.type)) })).filter((section) => section.tools.length > 0)
|
|
9785
9846
|
});
|
|
9786
|
-
const
|
|
9787
|
-
ownPointer(
|
|
9847
|
+
const toolbar = el("div", { class: "qc-surface qc-drawing-toolbar", role: "toolbar", "aria-orientation": "vertical", "aria-label": t("drawing.toolbar"), "data-role": "drawing-toolbar" });
|
|
9848
|
+
ownPointer(toolbar);
|
|
9788
9849
|
const column = el("div", { class: "qc-drawing-toolbar-column" });
|
|
9789
|
-
|
|
9850
|
+
toolbar.appendChild(column);
|
|
9790
9851
|
let closeFlyout = null;
|
|
9791
9852
|
let panelHost = deps.chrome;
|
|
9792
9853
|
const closeOpen = () => {
|
|
@@ -9832,16 +9893,16 @@ function mountDrawingToolbar(deps) {
|
|
|
9832
9893
|
};
|
|
9833
9894
|
const cell2 = (face, arrow) => {
|
|
9834
9895
|
const box = el("div", { class: "qc-drawing-cell" }, face);
|
|
9835
|
-
face.classList.add("qc-drawing-
|
|
9896
|
+
face.classList.add("qc-drawing-toolbar-button");
|
|
9836
9897
|
if (arrow) {
|
|
9837
|
-
arrow.classList.add("qc-drawing-
|
|
9898
|
+
arrow.classList.add("qc-drawing-toolbar-arrow");
|
|
9838
9899
|
arrow.setAttribute("aria-haspopup", "menu");
|
|
9839
9900
|
arrow.setAttribute("aria-expanded", "false");
|
|
9840
9901
|
box.appendChild(arrow);
|
|
9841
9902
|
}
|
|
9842
9903
|
return box;
|
|
9843
9904
|
};
|
|
9844
|
-
const divider = (where = "
|
|
9905
|
+
const divider = (where = "toolbar") => el("div", { class: `qc-separator qc-drawing-${where === "toolbar" ? "divider" : where === "flyout" ? "flyout-rule" : "menu-rule"}`, role: "separator" });
|
|
9845
9906
|
const cursorFace = button({ class: "qc-button", label: t("drawing.cursor"), onClick: () => deps.run("chart.drawings.arm", null) });
|
|
9846
9907
|
const cursorArrow = button({ class: "qc-button", label: t("drawing.cursorMenu"), icon: deps.icons.icon("chevronRight16") });
|
|
9847
9908
|
cursorArrow.addEventListener("click", () => {
|
|
@@ -9926,7 +9987,7 @@ function mountDrawingToolbar(deps) {
|
|
|
9926
9987
|
const face = button({ class: "qc-button", label: t(group.label) });
|
|
9927
9988
|
const arrow = button({ class: "qc-button", label: t("drawing.groupMenu", { group: t(group.label) }), icon: deps.icons.icon("chevronRight16") });
|
|
9928
9989
|
face.addEventListener("click", () => {
|
|
9929
|
-
const faceTool =
|
|
9990
|
+
const faceTool = drawingToolbarFaceOf(drawnGroup(group), deps.state().drawingToolbarTools);
|
|
9930
9991
|
if (group.id === "glyphs" || !faceTool) {
|
|
9931
9992
|
openGroup(group, arrow);
|
|
9932
9993
|
return;
|
|
@@ -9962,7 +10023,7 @@ function mountDrawingToolbar(deps) {
|
|
|
9962
10023
|
)
|
|
9963
10024
|
);
|
|
9964
10025
|
});
|
|
9965
|
-
const stay = button({ class: "qc-button qc-drawing-
|
|
10026
|
+
const stay = button({ class: "qc-button qc-drawing-toolbar-mode", label: t("drawing.stayInDrawingMode"), onClick: () => deps.run("chart.drawings.stayInMode", !deps.state().stayInDrawingMode) });
|
|
9966
10027
|
const lockAll = button({ class: "qc-button", label: t("drawing.lockAll"), onClick: () => deps.run("chart.drawings.lockAll", !deps.state().allLocked) });
|
|
9967
10028
|
column.append(cell2(magnetFace, magnetArrow), cell2(stay, null), cell2(lockAll, null));
|
|
9968
10029
|
const eyeFace = button({ class: "qc-button", label: t("drawing.hideDrawings"), onClick: () => deps.run("chart.drawings.hide", { mode: deps.state().hide.mode, on: !deps.state().hide.on }) });
|
|
@@ -10020,9 +10081,9 @@ function mountDrawingToolbar(deps) {
|
|
|
10020
10081
|
openFlyout(removeArrow, menu2(t("drawing.removeMenu"), ...items2, divider("menu"), policy));
|
|
10021
10082
|
});
|
|
10022
10083
|
column.append(cell2(removeFace, removeArrow));
|
|
10023
|
-
const favorites = button({ class: "qc-button qc-drawing-
|
|
10084
|
+
const favorites = button({ class: "qc-button qc-drawing-toolbar-mode", label: t("drawing.favToolsBar"), title: t("drawing.favTools"), onClick: () => deps.run("chart.drawings.favoritesBar", !deps.state().favorites.visible) });
|
|
10024
10085
|
column.append(el("div", { class: "qc-drawing-toolbar-end" }, cell2(favorites, null)));
|
|
10025
|
-
const unrove = rovingFocus(
|
|
10086
|
+
const unrove = rovingFocus(toolbar, () => [...column.querySelectorAll("button")].filter((b) => !b.closest("[hidden]")), "vertical");
|
|
10026
10087
|
const setActive = (b, active) => {
|
|
10027
10088
|
b.dataset.qcActive = String(active);
|
|
10028
10089
|
};
|
|
@@ -10045,7 +10106,7 @@ function mountDrawingToolbar(deps) {
|
|
|
10045
10106
|
for (const group of groups) {
|
|
10046
10107
|
const entry = groupFaces.get(group.id);
|
|
10047
10108
|
const drawn = drawnGroup(group);
|
|
10048
|
-
const faceTool = activeGroup === group.id && s.activeTool && toolShown(s.activeTool) ? s.activeTool :
|
|
10109
|
+
const faceTool = activeGroup === group.id && s.activeTool && toolShown(s.activeTool) ? s.activeTool : drawingToolbarFaceOf(drawn, s.drawingToolbarTools);
|
|
10049
10110
|
entry.face.replaceChildren(...[group.id === "glyphs" ? deps.icons.icon("groupGlyphs") : faceTool ? deps.icons.tool(faceTool) : null].filter((face) => face !== null));
|
|
10050
10111
|
const faceName = faceTool && group.id !== "glyphs" ? toolName(t, faceTool, faceTool) : t(group.label);
|
|
10051
10112
|
relabelButton(entry.face, faceName);
|
|
@@ -10085,7 +10146,7 @@ function mountDrawingToolbar(deps) {
|
|
|
10085
10146
|
gate(eyeArrow, "chart.drawings.hide");
|
|
10086
10147
|
if (s.layoutCharts > 1) {
|
|
10087
10148
|
if (!syncButton) {
|
|
10088
|
-
syncButton = button({ class: "qc-button qc-drawing-
|
|
10149
|
+
syncButton = button({ class: "qc-button qc-drawing-toolbar-button", label: t("drawing.syncLabel"), icon: deps.icons.icon("sync"), onClick: () => deps.run("chart.drawings.sync", !deps.state().sync) });
|
|
10089
10150
|
syncCell.appendChild(syncButton);
|
|
10090
10151
|
}
|
|
10091
10152
|
syncButton.title = t(s.sync ? "drawing.syncOnHelp" : "drawing.syncOffHelp");
|
|
@@ -10111,7 +10172,7 @@ function mountDrawingToolbar(deps) {
|
|
|
10111
10172
|
tidyRules(column, (child) => !child.hidden && [...child.querySelectorAll("button")].some((control) => !control.closest("[hidden]")));
|
|
10112
10173
|
};
|
|
10113
10174
|
const relabel = () => {
|
|
10114
|
-
|
|
10175
|
+
toolbar.setAttribute("aria-label", t("drawing.toolbar"));
|
|
10115
10176
|
relabelButton(cursorFace, t("drawing.cursor"));
|
|
10116
10177
|
relabelButton(cursorArrow, t("drawing.cursorMenu"));
|
|
10117
10178
|
for (const group of groups) relabelButton(groupFaces.get(group.id).arrow, t("drawing.groupMenu", { group: t(group.label) }));
|
|
@@ -10129,11 +10190,11 @@ function mountDrawingToolbar(deps) {
|
|
|
10129
10190
|
};
|
|
10130
10191
|
const mount = (container) => {
|
|
10131
10192
|
const nextPanelHost = panelHostFor(container, deps.chrome);
|
|
10132
|
-
if (
|
|
10193
|
+
if (toolbar.parentElement === container && panelHost === nextPanelHost) return;
|
|
10133
10194
|
closeOpen();
|
|
10134
10195
|
panelHost = nextPanelHost;
|
|
10135
|
-
|
|
10136
|
-
container?.appendChild(
|
|
10196
|
+
toolbar.remove();
|
|
10197
|
+
container?.appendChild(toolbar);
|
|
10137
10198
|
render();
|
|
10138
10199
|
};
|
|
10139
10200
|
mount(deps.container === void 0 ? deps.chrome : deps.container);
|
|
@@ -10147,7 +10208,7 @@ function mountDrawingToolbar(deps) {
|
|
|
10147
10208
|
unrove();
|
|
10148
10209
|
glyphPicker?.destroy();
|
|
10149
10210
|
glyphPicker = null;
|
|
10150
|
-
|
|
10211
|
+
toolbar.remove();
|
|
10151
10212
|
}
|
|
10152
10213
|
};
|
|
10153
10214
|
}
|
|
@@ -10283,9 +10344,9 @@ var ICONS = {
|
|
|
10283
10344
|
viewBox: "0 0 120 120",
|
|
10284
10345
|
body: '<path fill="currentColor" d="M113 2.5A7 7 0 0 1 120 9.5L120 71.5A6 6 0 0 1 114 77.5L83 77.5A3 3 0 0 1 80 74.5L80 42.5A4 4 0 0 1 84 38.5L110 38.5A4 4 0 0 0 114 34.5L114 12.5A4 4 0 0 0 110 8.5L10 8.5A4 4 0 0 0 6 12.5L6 34.5A4 4 0 0 0 10 38.5L36 38.5A4 4 0 0 1 40 42.5L40 74.5A3 3 0 0 1 37 77.5L6 77.5A6 6 0 0 1 0 71.5L0 9.5A7 7 0 0 1 7 2.5Z"/><path fill="currentColor" d="M40 77.5H80V110.5A7 7 0 0 1 73 117.5H47A7 7 0 0 1 40 110.5Z"/>'
|
|
10285
10346
|
},
|
|
10286
|
-
/**
|
|
10347
|
+
/** The market-status mark, on its native 18 grid. */
|
|
10287
10348
|
marketStatus: { viewBox: "0 0 18 18", body: '<circle cx="9" cy="9" r="7" fill="currentColor" opacity="0.2"/><path fill="currentColor" d="M9 5a4 4 0 1 1 0 8 4 4 0 0 1 0-8"/>' },
|
|
10288
|
-
/**
|
|
10349
|
+
/** The replay marks, on their native 18 grid. */
|
|
10289
10350
|
// The disc carries the phase in `currentColor`; the rewind stays ONE ink in both modes, because a
|
|
10290
10351
|
// mark that changed the colour of its own shape between modes would read as a different mark.
|
|
10291
10352
|
replayStatus: { viewBox: "0 0 18 18", body: '<circle cx="9" cy="9" r="9" fill="currentColor"/><path d="M8 5.5v7L4.5 9 8 5.5Zm5 0v7L9.5 9 13 5.5Z" fill="var(--qc-state-markInk)"/>' },
|
|
@@ -10314,18 +10375,18 @@ var ICONS = {
|
|
|
10314
10375
|
viewBox: "0 0 28 28",
|
|
10315
10376
|
body: fill("M18 14a4 4 0 1 1-8 0 4 4 0 0 1 8 0Zm-1 0a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z") + fill("M8.5 5h11l5 9-5 9h-11l-5-9 5-9Zm-3.86 9L9.1 6h9.82l4.45 8-4.45 8H9.1l-4.45-8Z")
|
|
10316
10377
|
},
|
|
10317
|
-
// Enter fullscreen:
|
|
10378
|
+
// Enter fullscreen: four corner brackets turning outward, drawn as a one-unit
|
|
10318
10379
|
// wall on the 28 grid.
|
|
10319
10380
|
fullscreen: { viewBox: "0 0 28 28", body: fill("M7 18.5A2.5 2.5 0 0 0 9.5 21H12v1H9.5A3.5 3.5 0 0 1 6 18.5V16h1zm15 0a3.5 3.5 0 0 1-3.5 3.5H16v-1h2.5a2.5 2.5 0 0 0 2.5-2.5V16h1zM12 7H9.5A2.5 2.5 0 0 0 7 9.5V12H6V9.5A3.5 3.5 0 0 1 9.5 6H12zm6.5-1A3.5 3.5 0 0 1 22 9.5V12h-1V9.5A2.5 2.5 0 0 0 18.5 7H16V6z") },
|
|
10320
|
-
// Exit fullscreen: the same brackets folded inward, stroked at
|
|
10381
|
+
// Exit fullscreen: the same brackets folded inward, stroked at a 1.5 weight.
|
|
10321
10382
|
exitFullscreen: { viewBox: "0 0 28 28", body: stroke("M11 6v1.5A3.5 3.5 0 0 1 7.5 11H6M22 11h-1.5A3.5 3.5 0 0 1 17 7.5V6M17 22v-1.5a3.5 3.5 0 0 1 3.5-3.5H22M6 17h1.5a3.5 3.5 0 0 1 3.5 3.5V22", 1.5) },
|
|
10322
|
-
// Chart image:
|
|
10383
|
+
// Chart image: a camera, a one-unit-walled body with the shutter hump left of centre
|
|
10323
10384
|
// and the lens ring offset toward it.
|
|
10324
10385
|
camera: {
|
|
10325
10386
|
viewBox: "0 0 28 28",
|
|
10326
10387
|
body: fill("M11.118 6a.5.5 0 0 0-.447.276L9.809 8H5.5A1.5 1.5 0 0 0 4 9.5v10A1.5 1.5 0 0 0 5.5 21h16a1.5 1.5 0 0 0 1.5-1.5v-10A1.5 1.5 0 0 0 21.5 8h-4.309l-.862-1.724A.5.5 0 0 0 15.882 6h-4.764zm-1.342-.17A1.5 1.5 0 0 1 11.118 5h4.764a1.5 1.5 0 0 1 1.342.83L17.809 7H21.5A2.5 2.5 0 0 1 24 9.5v10a2.5 2.5 0 0 1-2.5 2.5h-16A2.5 2.5 0 0 1 3 19.5v-10A2.5 2.5 0 0 1 5.5 7h3.691l.585-1.17z") + fill("M13.5 18a3.5 3.5 0 1 0 0-7 3.5 3.5 0 0 0 0 7zm0 1a4.5 4.5 0 1 0 0-9 4.5 4.5 0 0 0 0 9z")
|
|
10327
10388
|
},
|
|
10328
|
-
/** The layout tile's own maximize and restore marks, on
|
|
10389
|
+
/** The layout tile's own maximize and restore marks, on their native 18 grid: two
|
|
10329
10390
|
* opposite corner brackets turning outward to fill the layout, and inward to give the tile back. */
|
|
10330
10391
|
tileMaximize: { viewBox: "0 0 18 18", body: '<path fill="currentColor" d="M14.5 8V3.5H10V2h6v6h-1.5Zm-11 2v4.5H8V16H2v-6h1.5Z"/>' },
|
|
10331
10392
|
tileRestore: { viewBox: "0 0 18 18", body: '<path fill="currentColor" d="M10 8V2h1.5v4.5H16V8h-6Zm-2 2v6H6.5v-4.5H2V10h6Z"/>' },
|
|
@@ -10336,7 +10397,7 @@ var ICONS = {
|
|
|
10336
10397
|
navZoomIn: { viewBox: "0 0 18 18", body: '<path fill="currentColor" d="M8.25 13.75v-9.5h1.5v9.5h-1.5Z"/><path fill="currentColor" d="M13.75 9.75h-9.5v-1.5h9.5v1.5Z"/>' },
|
|
10337
10398
|
navScroll: { viewBox: "0 0 18 18", body: '<path fill="currentColor" d="M7.83 3.92 12.28 9l-4.45 5.08-1.13-1L10.29 9l-3.6-4.09 1.14-.99Z"/>' },
|
|
10338
10399
|
navReset: { viewBox: "0 0 18 18", body: '<path fill="currentColor" d="M10 6.38V8L6 5.5 10 3v1.85A5.25 5.25 0 1 1 3.75 10a.75.75 0 0 1 1.5 0A3.75 3.75 0 1 0 10 6.38Z"/>' },
|
|
10339
|
-
/** The pickers' wide caret, on its native 16 by 8 grid: the
|
|
10400
|
+
/** The pickers' wide caret, on its native 16 by 8 grid: the trigger arrow, which
|
|
10340
10401
|
* renders half size beside a picker's text. */
|
|
10341
10402
|
menuArrowWide: { viewBox: "0 0 16 8", body: '<path fill="currentColor" d="M0 1.475l7.396 6.04.596.485.593-.49L16 1.39 14.807 0 7.393 6.122 8.58 6.12 1.186.08z"/>' },
|
|
10342
10403
|
// Hairline weight: these two sit beside a label rather than alone in a 38px cell.
|
|
@@ -10447,7 +10508,7 @@ var ICONS = {
|
|
|
10447
10508
|
ruler: { viewBox: "0 0 28 28", body: '<path fill="currentColor" fill-rule="evenodd" transform="rotate(-45 14 14)" d="M2 9.75a1.5 1.5 0 0 0-1.5 1.5v5.5a1.5 1.5 0 0 0 1.5 1.5h24a1.5 1.5 0 0 0 1.5-1.5v-5.5a1.5 1.5 0 0 0-1.5-1.5zm0 1h3v2.5h1v-2.5h3.25v3.9h1v-3.9h3.25v2.5h1v-2.5h3.25v3.9h1v-3.9H22v2.5h1v-2.5h3a.5.5 0 0 1 .5.5v5.5a.5.5 0 0 1-.5.5H2a.5.5 0 0 1-.5-.5v-5.5a.5.5 0 0 1 .5-.5z"/>' },
|
|
10448
10509
|
zoomIn: { viewBox: "0 0 28 28", body: '<path fill="currentColor" d="M17.646 18.354l4 4 .708-.708-4-4z"/><path fill="currentColor" d="M12.5 21a8.5 8.5 0 1 1 0-17 8.5 8.5 0 0 1 0 17zm0-1a7.5 7.5 0 1 0 0-15 7.5 7.5 0 0 0 0 15z"/><path fill="currentColor" d="M9 13h7v-1H9z"/><path fill="currentColor" d="M13 16V9h-1v7z"/>' },
|
|
10449
10510
|
groupGlyphs: { viewBox: "0 0 28 28", body: '<path fill="currentColor" d="M4.05 14a9.95 9.95 0 1 1 19.9 0 9.95 9.95 0 0 1-19.9 0ZM14 3a11 11 0 1 0 0 22 11 11 0 0 0 0-22Zm-3 13.03a.5.5 0 0 1 .64.3 2.5 2.5 0 0 0 4.72 0 .5.5 0 0 1 .94.34 3.5 3.5 0 0 1-6.6 0 .5.5 0 0 1 .3-.64Zm.5-4.53a1 1 0 1 0 0 2 1 1 0 0 0 0-2Zm5 0a1 1 0 1 0 0 2 1 1 0 0 0 0-2Z"/>' },
|
|
10450
|
-
// The
|
|
10511
|
+
// The drawing toolbar's arrow: a filled chevron on a 10 by 16 grid, drawn 4 by 7 in the strip.
|
|
10451
10512
|
chevronRight16: { viewBox: "0 0 10 16", aspect: 4 / 7, size: 7, body: '<path fill="currentColor" d="M.6 1.4l1.4-1.4 8 8-8 8-1.4-1.4 6.389-6.532-6.389-6.668z"/>' },
|
|
10452
10513
|
// Six dots on an 8 by 12 grid, each PAINTED in currentColor: the svg the glyph is wrapped in
|
|
10453
10514
|
// carries `fill="none"`, so a rect with no fill of its own is a grip nobody can see.
|
|
@@ -10467,8 +10528,8 @@ var ICONS = {
|
|
|
10467
10528
|
textTee: { size: 15, aspect: 13 / 15, viewBox: "0 0 13 15", body: '<path stroke="currentColor" d="M4 14.5h2.5m2.5 0H6.5m0 0V.5m0 0h-5a1 1 0 0 0-1 1V4m6-3.5h5a1 1 0 0 1 1 1V4"/>' },
|
|
10468
10529
|
// A tool's star in a flyout: an outline at rest and a filled star once saved, on an 18 grid.
|
|
10469
10530
|
star: { viewBox: "0 0 18 18", body: '<path stroke="currentColor" d="M9 2.13l1.903 3.855.116.236.26.038 4.255.618-3.079 3.001-.188.184.044.259.727 4.237-3.805-2L9 12.434l-.233.122-3.805 2.001.727-4.237.044-.26-.188-.183-3.079-3.001 4.255-.618.26-.038.116-.236L9 2.13z"/>' },
|
|
10470
|
-
// The favorites bar's toggle on the
|
|
10471
|
-
// the button's fill.
|
|
10531
|
+
// The favorites bar's toggle on the drawing toolbar: one outlined star in either state, the state
|
|
10532
|
+
// carried by the button's fill.
|
|
10472
10533
|
favoritesBar: { viewBox: "0 0 28 28", body: '<path fill="currentColor" fill-rule="evenodd" d="m17.13 9.74 7.37.9-5.44 5.06L20.4 23 14 19.38 7.6 23l1.34-7.3-5.44-5.06 7.37-.9L14 3l3.13 6.74Zm5.11 1.63-4.26 3.97 1.04 5.74L14 18.24l-5.02 2.84 1.04-5.74-4.26-3.97 5.79-.7L14 5.37l2.45 5.3 5.8.7Z"/>' },
|
|
10473
10534
|
starFilled: { viewBox: "0 0 18 18", body: '<path fill="currentColor" d="M9 1l2.35 4.76 5.26.77-3.8 3.7.9 5.24L9 13l-4.7 2.47.9-5.23-3.8-3.71 5.25-.77L9 1z"/>' },
|
|
10474
10535
|
trash28: { viewBox: "0 0 28 28", body: '<path fill="currentColor" d="M18 7h5v1h-2.01l-1.33 14.64a1.5 1.5 0 0 1-1.5 1.36H9.84a1.5 1.5 0 0 1-1.49-1.36L7.01 8H5V7h5V6c0-1.1.9-2 2-2h4a2 2 0 0 1 2 2v1Zm-6-2a1 1 0 0 0-1 1v1h6V6a1 1 0 0 0-1-1h-4ZM8.02 8l1.32 14.54a.5.5 0 0 0 .5.46h8.33a.5.5 0 0 0 .5-.46L19.99 8H8.02Z"/>' },
|
|
@@ -10493,7 +10554,7 @@ var ICONS = {
|
|
|
10493
10554
|
submenuArrow: { viewBox: "0 0 24 24", body: '<path d="M9 18l6-6-6-6" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>' },
|
|
10494
10555
|
close18: { viewBox: "0 0 18 18", body: '<path d="M4.5 4.5l9 9M13.5 4.5l-9 9" stroke="currentColor" stroke-width="1.4" stroke-linecap="round"/>' },
|
|
10495
10556
|
chevronDown18: { viewBox: "0 0 18 18", body: '<path fill="currentColor" d="M3.92 7.83 9 12.29l5.08-4.46-1-1.13L9 10.29l-4.09-3.6-.99 1.14Z"/>' },
|
|
10496
|
-
// The indicator
|
|
10557
|
+
// The indicator picker's marks, on the grids its rows were drawn for.
|
|
10497
10558
|
pickerStar: { viewBox: "0 0 18 18", body: '<path fill="none" stroke="currentColor" stroke-width="1.2" stroke-linejoin="round" d="M9 2.13L11.057 6.168L15.534 6.877L12.329 10.082L13.038 14.558L9 12.5L4.962 14.558L5.671 10.082L2.466 6.877L6.943 6.168z"/>' },
|
|
10498
10559
|
pickerStarFilled: { viewBox: "0 0 18 18", body: '<path fill="currentColor" stroke="currentColor" stroke-width="1.2" stroke-linejoin="round" d="M9 2.13L11.057 6.168L15.534 6.877L12.329 10.082L13.038 14.558L9 12.5L4.962 14.558L5.671 10.082L2.466 6.877L6.943 6.168z"/>' },
|
|
10499
10560
|
plus24: { viewBox: "0 0 24 24", body: '<path d="M12 5v14M5 12h14" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>' },
|
|
@@ -10537,7 +10598,7 @@ function openDialog(options) {
|
|
|
10537
10598
|
if (options.width) box.style.width = `${options.width}px`;
|
|
10538
10599
|
stopPointer(box);
|
|
10539
10600
|
scrim.appendChild(box);
|
|
10540
|
-
|
|
10601
|
+
scrim.dataset.state = "opening";
|
|
10541
10602
|
let isOpen = true;
|
|
10542
10603
|
const previouslyFocused = document.activeElement;
|
|
10543
10604
|
const escape = pushEscapeOwner();
|
|
@@ -10562,7 +10623,7 @@ function openDialog(options) {
|
|
|
10562
10623
|
untrack();
|
|
10563
10624
|
document.removeEventListener("keydown", onKey, true);
|
|
10564
10625
|
previouslyFocused?.focus?.();
|
|
10565
|
-
const exitMs =
|
|
10626
|
+
const exitMs = closeOptions?.animate !== false ? motionDurationMs(scrim, "motion.durationBase") : 0;
|
|
10566
10627
|
if (exitMs <= 0) {
|
|
10567
10628
|
remove();
|
|
10568
10629
|
return;
|
|
@@ -10608,12 +10669,10 @@ function openDialog(options) {
|
|
|
10608
10669
|
const handle = { element: box, close, open: () => isOpen };
|
|
10609
10670
|
options.build(box, handle);
|
|
10610
10671
|
(layerFor(options.host) ?? options.host).appendChild(scrim);
|
|
10611
|
-
|
|
10612
|
-
|
|
10613
|
-
scrim.dataset.state = "open";
|
|
10614
|
-
}
|
|
10672
|
+
scrim.getBoundingClientRect();
|
|
10673
|
+
scrim.dataset.state = "open";
|
|
10615
10674
|
const refresh = options.refresh;
|
|
10616
|
-
untrack = trackOverlay(options.host, close, refresh ? () => {
|
|
10675
|
+
untrack = trackOverlay(options.host, () => close({ animate: false }), refresh ? () => {
|
|
10617
10676
|
if (isOpen) refresh();
|
|
10618
10677
|
} : void 0);
|
|
10619
10678
|
document.addEventListener("keydown", onKey, true);
|
|
@@ -11000,7 +11059,7 @@ function mountSettingsBar(deps) {
|
|
|
11000
11059
|
const at = deps.stackPosition();
|
|
11001
11060
|
return menuOf(t("drawing.visualOrder"), "narrow", ...ORDER_MOVES.map((move) => menuRow(t(move.label), () => deps.run(move.command), { disabled: move.dead(at), command: move.command })));
|
|
11002
11061
|
}
|
|
11003
|
-
return menuOf(t("drawing.
|
|
11062
|
+
return menuOf(t("drawing.visibilityOnTimeframes"), "wide", ...VISIBILITY_PRESETS.map((v) => menuRow(t(v.label), () => deps.run("chart.drawings.visibility", v.preset), { command: "chart.drawings.visibility" })));
|
|
11004
11063
|
};
|
|
11005
11064
|
const arm = (kind, row2) => {
|
|
11006
11065
|
cancelClose();
|
|
@@ -11034,7 +11093,7 @@ function mountSettingsBar(deps) {
|
|
|
11034
11093
|
t("drawing.moreActions"),
|
|
11035
11094
|
"wide",
|
|
11036
11095
|
submenuRow("order", t("drawing.visualOrder"), "layers"),
|
|
11037
|
-
submenuRow("visibility", t("drawing.
|
|
11096
|
+
submenuRow("visibility", t("drawing.visibilityOnTimeframes")),
|
|
11038
11097
|
separator2(),
|
|
11039
11098
|
plain(menuRow(t("drawing.clone"), () => deps.run("chart.drawings.clone"), { icon: deps.icons.icon("clone"), hint: t("drawing.hintClone", { modifier: modifier() }), command: "chart.drawings.clone" })),
|
|
11040
11099
|
plain(menuRow(t("drawing.copy"), () => deps.run("chart.drawings.copy"), { spacer: true, hint: t("drawing.hintCopy", { modifier: modifier() }), command: "chart.drawings.copy" })),
|
|
@@ -11377,11 +11436,15 @@ function openDialog2(options) {
|
|
|
11377
11436
|
stopDrag = dragBy(header, element);
|
|
11378
11437
|
element.append(header, body, footer);
|
|
11379
11438
|
},
|
|
11380
|
-
|
|
11439
|
+
// The drag and whatever the dialog opened over itself (a palette, a menu, a template prompt) go
|
|
11440
|
+
// as the dialog begins to close, so nothing answers the viewer from a box that no longer takes
|
|
11441
|
+
// input and no document listener outlives the box it belonged to.
|
|
11442
|
+
onClosing: () => {
|
|
11381
11443
|
stopDrag();
|
|
11382
11444
|
if (box) closeOverlays(box);
|
|
11383
|
-
options.
|
|
11384
|
-
}
|
|
11445
|
+
options.onClosing?.();
|
|
11446
|
+
},
|
|
11447
|
+
onClose: () => options.onClose?.()
|
|
11385
11448
|
});
|
|
11386
11449
|
return { body, footer, box: modal.element, close: modal.close };
|
|
11387
11450
|
}
|
|
@@ -11411,7 +11474,9 @@ function openImagePicker(deps) {
|
|
|
11411
11474
|
icons: deps.icons,
|
|
11412
11475
|
role: "drawing-image-picker",
|
|
11413
11476
|
width: 420,
|
|
11414
|
-
|
|
11477
|
+
// The picker stops taking a paste as it begins to close, and the Image tool can open it again
|
|
11478
|
+
// while the leaving one finishes its exit.
|
|
11479
|
+
onClosing: () => {
|
|
11415
11480
|
document.removeEventListener("paste", onPaste);
|
|
11416
11481
|
deps.onClose?.();
|
|
11417
11482
|
}
|
|
@@ -11901,7 +11966,7 @@ function visibilityRows(ctx) {
|
|
|
11901
11966
|
const range2 = visibility[key];
|
|
11902
11967
|
out.push(visibilityRangeRow(t, ctx.icons, { label: t(text2), range: range2, max, disabled: pinned(range2.on), onChange: (next) => ctx.patchVisibility({ [key]: next }) }));
|
|
11903
11968
|
}
|
|
11904
|
-
if (enabled === 1) out.push(el("span", { class: "qc-muted qc-drawing-note", text: t("drawing.
|
|
11969
|
+
if (enabled === 1) out.push(el("span", { class: "qc-muted qc-drawing-note", text: t("drawing.timeframePinnedNote") }));
|
|
11905
11970
|
return out;
|
|
11906
11971
|
}
|
|
11907
11972
|
|
|
@@ -11920,7 +11985,9 @@ function openSettingsDialog(deps) {
|
|
|
11920
11985
|
icons: deps.icons,
|
|
11921
11986
|
role: "drawing-settings",
|
|
11922
11987
|
width: 380,
|
|
11923
|
-
|
|
11988
|
+
// Escape, the corner close or a press outside ends the session as the dialog begins to close,
|
|
11989
|
+
// so the drawing is back as it was before the exit motion starts rather than after it.
|
|
11990
|
+
onClosing: () => {
|
|
11924
11991
|
if (settled) return;
|
|
11925
11992
|
cancel();
|
|
11926
11993
|
}
|
|
@@ -12128,7 +12195,7 @@ function attachDrawingsPlane(deps) {
|
|
|
12128
12195
|
indicators: { hidden: "drawing.statusIndicatorsHidden", shown: "drawing.statusIndicatorsShown" },
|
|
12129
12196
|
all: { hidden: "drawing.statusAllHidden", shown: "drawing.statusAllShown" }
|
|
12130
12197
|
};
|
|
12131
|
-
const groups =
|
|
12198
|
+
const groups = buildDrawingToolbarGroups();
|
|
12132
12199
|
const events = {};
|
|
12133
12200
|
const handle = attachDrawings({
|
|
12134
12201
|
chart: deps.chart,
|
|
@@ -12200,7 +12267,7 @@ function attachDrawingsPlane(deps) {
|
|
|
12200
12267
|
removeLocked: p.removeLocked,
|
|
12201
12268
|
counts: handle.counts(),
|
|
12202
12269
|
indicatorCount: deps.indicators.count(),
|
|
12203
|
-
|
|
12270
|
+
drawingToolbarTools: p.drawingToolbarTools,
|
|
12204
12271
|
favorites: p.favorites,
|
|
12205
12272
|
recentGlyphs: p.recentGlyphs,
|
|
12206
12273
|
layoutCharts: deps.chartCount()
|
|
@@ -12350,7 +12417,7 @@ function attachDrawingsPlane(deps) {
|
|
|
12350
12417
|
}
|
|
12351
12418
|
handle.armTool(tool, props);
|
|
12352
12419
|
if (tool) {
|
|
12353
|
-
const patch = {
|
|
12420
|
+
const patch = { drawingToolbarTools: rememberDrawingToolbarTool(groups, prefs().drawingToolbarTools, tool) };
|
|
12354
12421
|
const glyph2 = props && typeof props.glyph === "string" ? props.glyph : null;
|
|
12355
12422
|
if (glyph2) patch.recentGlyphs = pushRecentGlyph(prefs().recentGlyphs, glyph2);
|
|
12356
12423
|
write(patch);
|
|
@@ -12724,7 +12791,7 @@ var FillBetweenPrimitive = class {
|
|
|
12724
12791
|
lower = [];
|
|
12725
12792
|
colors;
|
|
12726
12793
|
/** Nothing until the first `setData`, which every render path makes before a draw. A tint chosen
|
|
12727
|
-
* here would be a second
|
|
12794
|
+
* here would be a second indicator default with no owner. */
|
|
12728
12795
|
color = "transparent";
|
|
12729
12796
|
attached(param) {
|
|
12730
12797
|
this.chart = param.chart;
|
|
@@ -12925,7 +12992,7 @@ function shapeKeyOf(built) {
|
|
|
12925
12992
|
return JSON.stringify([built.placement, built.plots.map((p) => [p.key, p.type, p.location === "absolute", p.scale ?? null])]);
|
|
12926
12993
|
}
|
|
12927
12994
|
function makeEntry(chart, built, neutral) {
|
|
12928
|
-
const paneIndex = built.placement === "pane" ?
|
|
12995
|
+
const paneIndex = built.placement === "pane" ? indicatorPaneIndex(chart) : 0;
|
|
12929
12996
|
const labels = built.display?.labelsOnPriceScale !== false;
|
|
12930
12997
|
const series2 = [];
|
|
12931
12998
|
const markers = [];
|
|
@@ -13124,7 +13191,7 @@ function safeRemove(chart, series2) {
|
|
|
13124
13191
|
} catch {
|
|
13125
13192
|
}
|
|
13126
13193
|
}
|
|
13127
|
-
function
|
|
13194
|
+
function indicatorPaneIndex(chart) {
|
|
13128
13195
|
sweepEmptyPanes(chart);
|
|
13129
13196
|
return chart.panes().length;
|
|
13130
13197
|
}
|
|
@@ -13693,7 +13760,7 @@ function attachIndicatorsPlane(deps) {
|
|
|
13693
13760
|
};
|
|
13694
13761
|
const renderer = attachIndicators(deps.chart, {
|
|
13695
13762
|
candles: deps.candleSeries,
|
|
13696
|
-
//
|
|
13763
|
+
// An indicator that declares no precision writes its scale through the symbol formatter, so a
|
|
13697
13764
|
// moving average on a Treasury reads in thirty-seconds like the bars beside it.
|
|
13698
13765
|
symbolPriceFormat: () => ({ key: deps.formatKey(), formatter: (price) => deps.formatter().format(price), minMove: deps.minMove() }),
|
|
13699
13766
|
neutral: () => deps.canvas().neutral
|
|
@@ -14562,7 +14629,7 @@ var GRAIN_GROUPS = [
|
|
|
14562
14629
|
{ unit: "d", rungs: [{ tf: "1d", sec: 86400 }] }
|
|
14563
14630
|
];
|
|
14564
14631
|
var DAY_RUNG = { tf: "1d", sec: 86400 };
|
|
14565
|
-
function
|
|
14632
|
+
function subTimeframesFor(chartTf) {
|
|
14566
14633
|
const parsed = parseTimeframe(chartTf);
|
|
14567
14634
|
const parent = tfSeconds(chartTf);
|
|
14568
14635
|
if (!parsed || !parent) return [];
|
|
@@ -14572,8 +14639,8 @@ function subIntervalsFor(chartTf) {
|
|
|
14572
14639
|
const intraday = parsed.unit === "s" || parsed.unit === "m" || parsed.unit === "h";
|
|
14573
14640
|
return GRAIN_GROUPS.filter((g, i) => i === own || i === own - 1 || intraday && g.unit === "s").flatMap((g) => g.rungs).filter((s) => s.sec <= parent && parent % s.sec === 0).sort((a, b) => a.sec - b.sec);
|
|
14574
14641
|
}
|
|
14575
|
-
function
|
|
14576
|
-
const offered =
|
|
14642
|
+
function autoTimeframeFor(chartTf) {
|
|
14643
|
+
const offered = subTimeframesFor(chartTf);
|
|
14577
14644
|
return offered.length ? offered[offered.length - 1] : null;
|
|
14578
14645
|
}
|
|
14579
14646
|
function composeFormingBar(parent, subs, k) {
|
|
@@ -14595,8 +14662,8 @@ function attachReplayPlane(deps) {
|
|
|
14595
14662
|
let arming = false;
|
|
14596
14663
|
let timer = null;
|
|
14597
14664
|
let speed = deps.initialSpeed;
|
|
14598
|
-
let
|
|
14599
|
-
let
|
|
14665
|
+
let autoGrain = deps.initialGrain === "auto";
|
|
14666
|
+
let manualGrain = autoGrain ? null : deps.initialGrain;
|
|
14600
14667
|
let subs = null;
|
|
14601
14668
|
let formK = 0;
|
|
14602
14669
|
let stepping = null;
|
|
@@ -14608,18 +14675,18 @@ function attachReplayPlane(deps) {
|
|
|
14608
14675
|
};
|
|
14609
14676
|
const grains = () => {
|
|
14610
14677
|
const served = deps.resolutions();
|
|
14611
|
-
const ladder =
|
|
14678
|
+
const ladder = subTimeframesFor(deps.timeframe());
|
|
14612
14679
|
return served && served.length > 0 ? ladder.filter((s) => served.includes(s.tf)) : ladder;
|
|
14613
14680
|
};
|
|
14614
|
-
const
|
|
14681
|
+
const effectiveGrain = () => {
|
|
14615
14682
|
const available = grains();
|
|
14616
|
-
if (
|
|
14617
|
-
return available.find((s) => s.tf ===
|
|
14683
|
+
if (autoGrain) return available.length ? available[available.length - 1] : null;
|
|
14684
|
+
return available.find((s) => s.tf === manualGrain) ?? null;
|
|
14618
14685
|
};
|
|
14619
14686
|
const sync = () => {
|
|
14620
14687
|
deps.onChange();
|
|
14621
14688
|
};
|
|
14622
|
-
const
|
|
14689
|
+
const currentGrain = () => autoGrain ? "auto" : manualGrain ?? "auto";
|
|
14623
14690
|
const paintCursor = () => {
|
|
14624
14691
|
if (!master) return;
|
|
14625
14692
|
deps.paint(master.slice(0, cursor));
|
|
@@ -14654,15 +14721,15 @@ function attachReplayPlane(deps) {
|
|
|
14654
14721
|
deps.setCrosshair(!next);
|
|
14655
14722
|
};
|
|
14656
14723
|
const fetchSubs = async (parentIdx) => {
|
|
14657
|
-
const
|
|
14658
|
-
if (!master || !
|
|
14659
|
-
if (
|
|
14724
|
+
const grain = effectiveGrain();
|
|
14725
|
+
if (!master || !grain) return null;
|
|
14726
|
+
if (grain.sec >= tfSeconds(deps.timeframe())) return null;
|
|
14660
14727
|
const parent = master[parentIdx];
|
|
14661
14728
|
if (!parent) return null;
|
|
14662
14729
|
const from = parent.t;
|
|
14663
14730
|
const to = parent.t + tfSeconds(deps.timeframe()) - 1;
|
|
14664
14731
|
try {
|
|
14665
|
-
const page = await deps.datafeed.history(deps.symbol(),
|
|
14732
|
+
const page = await deps.datafeed.history(deps.symbol(), grain.tf, { from, to });
|
|
14666
14733
|
const found = page.bars.filter((b) => b.t >= from && b.t <= to);
|
|
14667
14734
|
return found.length >= 2 ? found : null;
|
|
14668
14735
|
} catch {
|
|
@@ -14693,11 +14760,11 @@ function attachReplayPlane(deps) {
|
|
|
14693
14760
|
const epoch = generation;
|
|
14694
14761
|
const symbol = deps.symbol();
|
|
14695
14762
|
const timeframe2 = deps.timeframe();
|
|
14696
|
-
const grain =
|
|
14697
|
-
const chosen =
|
|
14763
|
+
const grain = currentGrain();
|
|
14764
|
+
const chosen = effectiveGrain();
|
|
14698
14765
|
const wholeBar = !chosen || chosen.sec >= tfSeconds(deps.timeframe());
|
|
14699
14766
|
const found = wholeBar ? null : await fetchSubs(targetCursor - 1);
|
|
14700
|
-
if (destroyed || deps.disposed() || stepping !== flight || generation !== epoch || deps.symbol() !== symbol || deps.timeframe() !== timeframe2 ||
|
|
14767
|
+
if (destroyed || deps.disposed() || stepping !== flight || generation !== epoch || deps.symbol() !== symbol || deps.timeframe() !== timeframe2 || currentGrain() !== grain || !master || cursor !== targetCursor - 1 || master[targetCursor - 1] !== parent) return;
|
|
14701
14768
|
cursor = targetCursor;
|
|
14702
14769
|
if (found) {
|
|
14703
14770
|
subs = found;
|
|
@@ -14796,26 +14863,26 @@ function attachReplayPlane(deps) {
|
|
|
14796
14863
|
paintCursor();
|
|
14797
14864
|
sync();
|
|
14798
14865
|
},
|
|
14799
|
-
|
|
14866
|
+
timeframe: currentGrain,
|
|
14800
14867
|
// What `auto` actually RESOLVED to, so a surface can say the grain rather than the mode. Empty
|
|
14801
14868
|
// when the chart's timeframe has nothing finer to form from and updates advance whole bars.
|
|
14802
|
-
|
|
14803
|
-
|
|
14804
|
-
if (token ===
|
|
14869
|
+
resolvedTimeframe: () => effectiveGrain()?.tf ?? "",
|
|
14870
|
+
setTimeframe(token) {
|
|
14871
|
+
if (token === currentGrain()) return;
|
|
14805
14872
|
if (token === "auto") {
|
|
14806
|
-
|
|
14873
|
+
autoGrain = true;
|
|
14807
14874
|
} else {
|
|
14808
14875
|
if (!grains().some((s) => s.tf === token)) return;
|
|
14809
|
-
|
|
14810
|
-
|
|
14876
|
+
autoGrain = false;
|
|
14877
|
+
manualGrain = token;
|
|
14811
14878
|
}
|
|
14812
14879
|
invalidateStep();
|
|
14813
|
-
deps.persist("
|
|
14880
|
+
deps.persist("timeframe", currentGrain());
|
|
14814
14881
|
subs = null;
|
|
14815
14882
|
formK = 0;
|
|
14816
14883
|
sync();
|
|
14817
14884
|
},
|
|
14818
|
-
|
|
14885
|
+
subTimeframes: () => grains().map((s) => s.tf),
|
|
14819
14886
|
state: () => ({ on: master !== null, playing, cursor, total: master?.length ?? deps.bars().length, speed }),
|
|
14820
14887
|
// An OPEN QUESTION outranks a running session. Re-arming mid-replay to choose a different start
|
|
14821
14888
|
// puts the plot back to taking a click, and every surface that answers to the picker — the
|
|
@@ -14852,7 +14919,7 @@ function attachReplayPlane(deps) {
|
|
|
14852
14919
|
abandon,
|
|
14853
14920
|
snapshot: () => ({ on: master !== null, playing, cursor: master === null ? deps.bars().length : cursor, total: master?.length ?? deps.bars().length }),
|
|
14854
14921
|
speed: () => speed,
|
|
14855
|
-
|
|
14922
|
+
timeframe: currentGrain,
|
|
14856
14923
|
destroy() {
|
|
14857
14924
|
destroyed = true;
|
|
14858
14925
|
abandon();
|
|
@@ -14931,7 +14998,7 @@ function createExtensionHost(deps, extensions = []) {
|
|
|
14931
14998
|
}
|
|
14932
14999
|
}
|
|
14933
15000
|
clearLanes(record3.lanes);
|
|
14934
|
-
record3.
|
|
15001
|
+
record3.menuBuilders.clear();
|
|
14935
15002
|
if (record3.hideLayers.size) {
|
|
14936
15003
|
record3.hideLayers.clear();
|
|
14937
15004
|
notifyHideLayers();
|
|
@@ -14955,7 +15022,7 @@ function createExtensionHost(deps, extensions = []) {
|
|
|
14955
15022
|
} },
|
|
14956
15023
|
live: true,
|
|
14957
15024
|
lanes: newLanes(),
|
|
14958
|
-
|
|
15025
|
+
menuBuilders: /* @__PURE__ */ new Set(),
|
|
14959
15026
|
hideLayers: /* @__PURE__ */ new Map(),
|
|
14960
15027
|
commands: /* @__PURE__ */ new Map(),
|
|
14961
15028
|
priceLines: /* @__PURE__ */ new Set(),
|
|
@@ -15044,12 +15111,12 @@ function createExtensionHost(deps, extensions = []) {
|
|
|
15044
15111
|
record3.lanes.dispose.delete(callback);
|
|
15045
15112
|
};
|
|
15046
15113
|
},
|
|
15047
|
-
contributeContextMenu(
|
|
15114
|
+
contributeContextMenu(build) {
|
|
15048
15115
|
if (!record3.live) return () => {
|
|
15049
15116
|
};
|
|
15050
|
-
record3.
|
|
15117
|
+
record3.menuBuilders.add(build);
|
|
15051
15118
|
return () => {
|
|
15052
|
-
record3.
|
|
15119
|
+
record3.menuBuilders.delete(build);
|
|
15053
15120
|
};
|
|
15054
15121
|
},
|
|
15055
15122
|
contributeCommands(commands) {
|
|
@@ -15143,9 +15210,9 @@ function createExtensionHost(deps, extensions = []) {
|
|
|
15143
15210
|
if (!hostLive) return [];
|
|
15144
15211
|
const rows2 = [];
|
|
15145
15212
|
for (const record3 of liveRecords()) {
|
|
15146
|
-
for (const
|
|
15213
|
+
for (const build of [...record3.menuBuilders]) {
|
|
15147
15214
|
try {
|
|
15148
|
-
rows2.push(...
|
|
15215
|
+
rows2.push(...build(context));
|
|
15149
15216
|
} catch {
|
|
15150
15217
|
}
|
|
15151
15218
|
}
|
|
@@ -15540,7 +15607,7 @@ function mountChartLegend(container, strings, controls) {
|
|
|
15540
15607
|
venueText.className = "qc-legend-venue qc-legend-row-venue";
|
|
15541
15608
|
const value = document.createElement("span");
|
|
15542
15609
|
value.className = "qc-legend-value";
|
|
15543
|
-
value.dataset.role = "legend-
|
|
15610
|
+
value.dataset.role = "legend-indicator-value";
|
|
15544
15611
|
const gear = chipButton(controls.icons.glyph(ICONS.legendSettings, { size: 18 }), "", () => {
|
|
15545
15612
|
const r = gear.getBoundingClientRect();
|
|
15546
15613
|
controls.onSettings?.(chip.id, { x: r.x, y: r.y, w: r.width, h: r.height });
|
|
@@ -16195,7 +16262,7 @@ function attachLegendPlane(deps) {
|
|
|
16195
16262
|
const paneRemembered = /* @__PURE__ */ new Map();
|
|
16196
16263
|
const paneKeys = () => {
|
|
16197
16264
|
const keys = { 0: "main" };
|
|
16198
|
-
for (const [id, pane] of Object.entries(deps.indicators.renderer.paneOf())) if (pane > 0) keys[pane] = `
|
|
16265
|
+
for (const [id, pane] of Object.entries(deps.indicators.renderer.paneOf())) if (pane > 0) keys[pane] = `indicator:${id}`;
|
|
16199
16266
|
for (const entry of deps.compare?.api.list() ?? []) {
|
|
16200
16267
|
const pane = deps.compare.handle.paneIndexOf(entry.symbol);
|
|
16201
16268
|
if (pane !== null && pane > 0) keys[pane] = `compare:${entry.symbol}`;
|
|
@@ -16320,7 +16387,7 @@ function attachLegendPlane(deps) {
|
|
|
16320
16387
|
legend2?.setChips([
|
|
16321
16388
|
...deps.indicators.chipsAt(hovered).map((row2) => {
|
|
16322
16389
|
const paneIndex = paneOf[row2.id] ?? 0;
|
|
16323
|
-
const remembered = paneRemembered.get(`
|
|
16390
|
+
const remembered = paneRemembered.get(`indicator:${row2.id}`);
|
|
16324
16391
|
return {
|
|
16325
16392
|
...row2,
|
|
16326
16393
|
paneIndex,
|
|
@@ -16699,7 +16766,7 @@ var CONTROL_ICONS = byMeaning({
|
|
|
16699
16766
|
chevronDown: [ICONS.chevronDown, ICONS.chevronDown18],
|
|
16700
16767
|
chevronLeft: [ICONS.chevronLeft],
|
|
16701
16768
|
chevronRight: [ICONS.chevronRight],
|
|
16702
|
-
// The
|
|
16769
|
+
// The drawing toolbar's arrow, pointing where the flyout it opens will stand.
|
|
16703
16770
|
flyout: [ICONS.chevronRight16],
|
|
16704
16771
|
submenu: [ICONS.submenuArrow],
|
|
16705
16772
|
more: [ICONS.kebab],
|
|
@@ -16725,7 +16792,7 @@ var CONTROL_ICONS = byMeaning({
|
|
|
16725
16792
|
selectDate: [ICONS.calendar],
|
|
16726
16793
|
firstAvailable: [ICONS.firstAvailable],
|
|
16727
16794
|
randomBar: [ICONS.randomBar],
|
|
16728
|
-
// The drawing
|
|
16795
|
+
// The drawing toolbar, its menus, the favorites bar and the drawing settings.
|
|
16729
16796
|
cursorCross: [ICONS.cursorCross],
|
|
16730
16797
|
cursorDot: [ICONS.cursorDot],
|
|
16731
16798
|
cursorArrow: [ICONS.cursorArrow],
|
|
@@ -17116,7 +17183,7 @@ function attachMenuPlane(deps) {
|
|
|
17116
17183
|
{
|
|
17117
17184
|
priceText,
|
|
17118
17185
|
symbol: deps.symbol(),
|
|
17119
|
-
// The
|
|
17186
|
+
// The context menu's paste row is live exactly when the paste command would run (a copied
|
|
17120
17187
|
// drawing on the clipboard, the verb permitted). It offers no settings row: the settings
|
|
17121
17188
|
// dialog belongs to the selected drawing's own surfaces, which the drawing plane mounts
|
|
17122
17189
|
// and the chart.drawings.* commands drive.
|
|
@@ -17886,12 +17953,12 @@ function registerChartCommands(deps) {
|
|
|
17886
17953
|
}
|
|
17887
17954
|
});
|
|
17888
17955
|
add({
|
|
17889
|
-
id: "chart.replay.
|
|
17956
|
+
id: "chart.replay.setTimeframe",
|
|
17890
17957
|
scope: "chart",
|
|
17891
|
-
label: "command.
|
|
17958
|
+
label: "command.replayTimeframe",
|
|
17892
17959
|
available: () => handle.replay.state().on,
|
|
17893
17960
|
execute: (arg) => {
|
|
17894
|
-
if (typeof arg === "string" && arg) handle.replay.
|
|
17961
|
+
if (typeof arg === "string" && arg) handle.replay.setTimeframe(arg);
|
|
17895
17962
|
}
|
|
17896
17963
|
});
|
|
17897
17964
|
const servable = (token) => {
|
|
@@ -18220,7 +18287,7 @@ var SCALE_KEY = "quickcharts.scale.v1";
|
|
|
18220
18287
|
var PRICE_AXIS_KEY = "quickcharts.priceAxis.v1";
|
|
18221
18288
|
var HIDDEN_KEY = "quickcharts.indHidden.v1";
|
|
18222
18289
|
var REPLAY_SPEED_KEY = "quickcharts.replaySpeed.v1";
|
|
18223
|
-
var
|
|
18290
|
+
var REPLAY_TIMEFRAME_KEY = "quickcharts.replayTf.v1";
|
|
18224
18291
|
var TIMEZONE_KEY = "quickcharts.timezone.v1";
|
|
18225
18292
|
var SUBSESSION_KEY = "quickcharts.subsession.v1";
|
|
18226
18293
|
function sameIds(left, right) {
|
|
@@ -18465,8 +18532,8 @@ function createChartInstance(deps) {
|
|
|
18465
18532
|
const indicators2 = attachIndicatorsPlane({
|
|
18466
18533
|
chart,
|
|
18467
18534
|
candleSeries,
|
|
18468
|
-
//
|
|
18469
|
-
// it, so it computes over the painted model rather than the loaded one.
|
|
18535
|
+
// An indicator drawn over bars the active subsession hides would not line up with the series
|
|
18536
|
+
// beside it, so it computes over the painted model rather than the loaded one.
|
|
18470
18537
|
bars: () => shownBars(),
|
|
18471
18538
|
i18n,
|
|
18472
18539
|
formatter: () => symbolFormatter,
|
|
@@ -18622,9 +18689,9 @@ function createChartInstance(deps) {
|
|
|
18622
18689
|
// A chart whose crosshair the host switched OFF keeps it off: replay may take the crosshair
|
|
18623
18690
|
// away for the length of a question, never hand one back that was never there.
|
|
18624
18691
|
setCrosshair: (visible) => chart.applyOptions({ crosshair: { mode: visible && deps.features.crosshair ? CrosshairMode.Normal : CrosshairMode.Hidden } }),
|
|
18625
|
-
// The feed's own grains: a chart never offers, or fetches,
|
|
18692
|
+
// The feed's own grains: a chart never offers, or fetches, a timeframe its feed cannot serve.
|
|
18626
18693
|
resolutions: () => deps.capabilities().resolutions,
|
|
18627
|
-
persist: (key, value) => storage.set(key === "speed" ? REPLAY_SPEED_KEY :
|
|
18694
|
+
persist: (key, value) => storage.set(key === "speed" ? REPLAY_SPEED_KEY : REPLAY_TIMEFRAME_KEY, value),
|
|
18628
18695
|
onChange: () => {
|
|
18629
18696
|
const state = replay2.snapshot();
|
|
18630
18697
|
countdown?.refresh();
|
|
@@ -18634,7 +18701,7 @@ function createChartInstance(deps) {
|
|
|
18634
18701
|
events.emit("replay", state);
|
|
18635
18702
|
},
|
|
18636
18703
|
initialSpeed: coerceReplaySpeed(storage.get(REPLAY_SPEED_KEY) ?? deps.preferences.replaySpeed),
|
|
18637
|
-
|
|
18704
|
+
initialGrain: storage.get(REPLAY_TIMEFRAME_KEY) ?? deps.preferences.replayTimeframe ?? "auto"
|
|
18638
18705
|
});
|
|
18639
18706
|
const countdownClock = createCountdownClock(() => Date.now() / 1e3, datafeed.serverTime?.bind(datafeed));
|
|
18640
18707
|
countdown = attachCountdown({
|
|
@@ -18681,7 +18748,8 @@ function createChartInstance(deps) {
|
|
|
18681
18748
|
setTouchAction: (value) => {
|
|
18682
18749
|
gestures.style.touchAction = value;
|
|
18683
18750
|
},
|
|
18684
|
-
// A contributed layer rides the same eye the
|
|
18751
|
+
// A contributed layer rides the same eye the drawing toolbar drives, through the drawing
|
|
18752
|
+
// plane's verbs.
|
|
18685
18753
|
hideState: () => drawings2.verbs?.hide() ?? { mode: "drawings", on: false },
|
|
18686
18754
|
setHide: (state) => drawings2.verbs?.setHide(state),
|
|
18687
18755
|
hideLayersChanged: () => drawings2.syncHideLayers()
|
|
@@ -19357,7 +19425,7 @@ function createChartInstance(deps) {
|
|
|
19357
19425
|
resetAppearance,
|
|
19358
19426
|
t: () => i18n.t,
|
|
19359
19427
|
// What is PAINTED, which is the replay slice while replay is on: the data export writes what
|
|
19360
|
-
// the
|
|
19428
|
+
// the viewer can see and never a bar the cursor has not revealed.
|
|
19361
19429
|
bars: () => shownBars(),
|
|
19362
19430
|
// The feed's own statement of how deep its history goes, never the oldest bar that happens to
|
|
19363
19431
|
// be loaded: the chart opens on a short window, and a preset judged against that would be
|
|
@@ -19623,7 +19691,14 @@ function validGeometry(value, catalog) {
|
|
|
19623
19691
|
}
|
|
19624
19692
|
|
|
19625
19693
|
// src/widget/layout.ts
|
|
19626
|
-
var SYNC_OFF = { symbol: false,
|
|
19694
|
+
var SYNC_OFF = { symbol: false, timeframe: false, crosshair: false, time: false, dateRange: false };
|
|
19695
|
+
var syncSwitches = (stated) => Object.fromEntries(LAYOUT_SYNC_KEYS.map((key) => [key, stated[key] ?? SYNC_OFF[key]]));
|
|
19696
|
+
function readSyncFlags(value, defaults) {
|
|
19697
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
19698
|
+
const stated = value;
|
|
19699
|
+
if (LAYOUT_SYNC_KEYS.some((key) => key in stated && typeof stated[key] !== "boolean")) return null;
|
|
19700
|
+
return Object.fromEntries(LAYOUT_SYNC_KEYS.map((key) => [key, key in stated ? stated[key] : defaults[key]]));
|
|
19701
|
+
}
|
|
19627
19702
|
var LAYOUT_CONTENT_VERSION = 2;
|
|
19628
19703
|
var validEntityNamespace = (value) => /^(?:layout:(?:[A-Za-z0-9_.!~*'()-]|%[0-9A-F]{2})+|session:[a-z0-9-]+)$/.test(value);
|
|
19629
19704
|
var cloneIndicators = (instances) => instances.map((instance) => ({
|
|
@@ -19638,7 +19713,7 @@ var cloneTemplate = (template) => ({
|
|
|
19638
19713
|
});
|
|
19639
19714
|
function createLayoutPlane(deps) {
|
|
19640
19715
|
const slots = [];
|
|
19641
|
-
let flags =
|
|
19716
|
+
let flags = syncSwitches(deps.sync ?? {});
|
|
19642
19717
|
const fixedSync = deps.syncOffered ? LAYOUT_SYNC_KEYS.filter((key) => !deps.syncOffered.includes(key)) : [];
|
|
19643
19718
|
const fixedFlags = { ...flags };
|
|
19644
19719
|
const holdFixed = (next) => {
|
|
@@ -19757,7 +19832,7 @@ function createLayoutPlane(deps) {
|
|
|
19757
19832
|
if (!applying) emitActive();
|
|
19758
19833
|
}));
|
|
19759
19834
|
slot.unsubscribes.push(handle.on("timeframe", () => {
|
|
19760
|
-
if (flags.
|
|
19835
|
+
if (flags.timeframe) fanOut2(index(), (other) => other.setTimeframe(handle.timeframe()));
|
|
19761
19836
|
if (!applying) deps.onChange();
|
|
19762
19837
|
}));
|
|
19763
19838
|
};
|
|
@@ -20021,15 +20096,14 @@ function createLayoutPlane(deps) {
|
|
|
20021
20096
|
if (!arrangement2) throw new Error(`unknown arrangement code ${String(c.arrangement)}`);
|
|
20022
20097
|
if (charts.length !== arrangement2.count) throw new Error("layout chart count does not match arrangement");
|
|
20023
20098
|
if (!validGeometry(c.geometry, arrangement2.rects)) throw new Error("invalid layout geometry");
|
|
20024
|
-
const sync = c.sync
|
|
20025
|
-
|
|
20026
|
-
if (!sync || keys.some((key) => typeof sync[key] !== "boolean")) throw new Error("invalid layout sync flags");
|
|
20099
|
+
const sync = readSyncFlags(c.sync, fixedFlags);
|
|
20100
|
+
if (!sync) throw new Error("invalid layout sync flags");
|
|
20027
20101
|
if (!Number.isInteger(c.active) || c.active < 0 || c.active >= charts.length) throw new Error("invalid active chart");
|
|
20028
20102
|
return {
|
|
20029
20103
|
arrangement: arrangement2,
|
|
20030
20104
|
identity: { namespace: identity.namespace, next: identity.next },
|
|
20031
20105
|
geometry: c.geometry.map((rect) => ({ ...rect })),
|
|
20032
|
-
sync
|
|
20106
|
+
sync,
|
|
20033
20107
|
active: c.active,
|
|
20034
20108
|
charts
|
|
20035
20109
|
};
|
|
@@ -20111,7 +20185,7 @@ function createLayoutPlane(deps) {
|
|
|
20111
20185
|
setSync(partial) {
|
|
20112
20186
|
if (removed) return;
|
|
20113
20187
|
if (fixedSync.length > 0 && Object.keys(partial).every((key) => fixedSync.includes(key))) return;
|
|
20114
|
-
flags = holdFixed({ ...flags, ...partial });
|
|
20188
|
+
flags = holdFixed(syncSwitches({ ...flags, ...partial }));
|
|
20115
20189
|
deps.onChange();
|
|
20116
20190
|
emitCommitted();
|
|
20117
20191
|
},
|
|
@@ -20759,9 +20833,9 @@ var FLYOUT_WIDTH = {
|
|
|
20759
20833
|
session: 176,
|
|
20760
20834
|
/** The replay row's starting-point choices, opening upward. */
|
|
20761
20835
|
replayStart: 177,
|
|
20762
|
-
/** The replay row's update-
|
|
20763
|
-
* its rates. */
|
|
20764
|
-
|
|
20836
|
+
/** The replay row's update-timeframe list. The speed list opens at the same width and grows to
|
|
20837
|
+
* fit its rates. */
|
|
20838
|
+
replayTimeframe: 196
|
|
20765
20839
|
};
|
|
20766
20840
|
|
|
20767
20841
|
// src/ui/chrome/bottomBar.ts
|
|
@@ -21235,7 +21309,8 @@ function openIndicatorPicker(deps) {
|
|
|
21235
21309
|
className: "qc-picker-dialog",
|
|
21236
21310
|
width: 840,
|
|
21237
21311
|
refresh: () => redraw(),
|
|
21238
|
-
|
|
21312
|
+
// The host's listing is abandoned as the dialog begins to close, not when its exit ends.
|
|
21313
|
+
onClosing: () => {
|
|
21239
21314
|
cancel();
|
|
21240
21315
|
offLocale();
|
|
21241
21316
|
},
|
|
@@ -21547,7 +21622,7 @@ var PLACEMENTS2 = [
|
|
|
21547
21622
|
];
|
|
21548
21623
|
function rowLabels(row2) {
|
|
21549
21624
|
const names = symbolNames(row2);
|
|
21550
|
-
return { ticker: names.mark, description: names.description, source: row2.exchange || row2.
|
|
21625
|
+
return { ticker: names.mark, description: names.description, source: row2.exchange || row2.dataSource || "" };
|
|
21551
21626
|
}
|
|
21552
21627
|
function offeredOperators(spreads) {
|
|
21553
21628
|
if (spreads === false) return [];
|
|
@@ -21776,10 +21851,10 @@ function buildSearchSurface(deps, box, frame) {
|
|
|
21776
21851
|
option.appendChild(mark2);
|
|
21777
21852
|
} else {
|
|
21778
21853
|
const source3 = h("span", { class: "qc-search-source" });
|
|
21779
|
-
source3.appendChild(h("span", { class: "qc-search-source-text" }, label2.source ? h("span", { class: "qc-search-
|
|
21854
|
+
source3.appendChild(h("span", { class: "qc-search-source-text" }, label2.source ? h("span", { class: "qc-search-source-name" }, label2.source) : null, h("span", { class: "qc-search-kind" }, r.type)));
|
|
21780
21855
|
if (label2.source) {
|
|
21781
21856
|
const sourceMark = h("span", { class: "qc-search-mark", "aria-hidden": "true" });
|
|
21782
|
-
const dropSource = r.exchange ? deps.painters.venue?.({ exchange: r.exchange, host: sourceMark, size: SOURCE_MARK_SIZE }) : r.
|
|
21857
|
+
const dropSource = r.exchange ? deps.painters.venue?.({ exchange: r.exchange, host: sourceMark, size: SOURCE_MARK_SIZE }) : r.dataSource ? deps.painters.dataSource?.({ dataSource: r.dataSource, host: sourceMark, size: SOURCE_MARK_SIZE }) : void 0;
|
|
21783
21858
|
if (typeof dropSource === "function") {
|
|
21784
21859
|
sourceMark.dataset.qcHost = "true";
|
|
21785
21860
|
markDisposers.push(dropSource);
|
|
@@ -21886,7 +21961,6 @@ function openSearchDialog(deps) {
|
|
|
21886
21961
|
host: deps.host,
|
|
21887
21962
|
label: title,
|
|
21888
21963
|
className: `qc-search-dialog qc-search-dialog--${mode}`,
|
|
21889
|
-
animated: true,
|
|
21890
21964
|
// The compare family (compare, and a compare row's change-symbol) shares one role for a host
|
|
21891
21965
|
// test to find; a symbol search, with or without a chart behind it, is the other.
|
|
21892
21966
|
role: mode === "compare" || mode === "change-symbol" ? "compare-dialog" : "symbol-search",
|
|
@@ -22021,7 +22095,7 @@ var SORT_LABEL = {
|
|
|
22021
22095
|
"modified-desc": "layouts.sortModifiedDesc"
|
|
22022
22096
|
};
|
|
22023
22097
|
function listingFacts(t, row2) {
|
|
22024
|
-
return row2.symbol ? row2.timeframe ? t("layouts.listingFacts", { symbol: row2.symbol,
|
|
22098
|
+
return row2.symbol ? row2.timeframe ? t("layouts.listingFacts", { symbol: row2.symbol, timeframe: timeframeChipLabel(row2.timeframe) }) : row2.symbol : null;
|
|
22025
22099
|
}
|
|
22026
22100
|
function mountLayoutDialogs(deps) {
|
|
22027
22101
|
const t = () => deps.i18n.t;
|
|
@@ -22037,7 +22111,7 @@ function mountLayoutDialogs(deps) {
|
|
|
22037
22111
|
};
|
|
22038
22112
|
const askName = (question) => {
|
|
22039
22113
|
raise();
|
|
22040
|
-
asking?.close();
|
|
22114
|
+
asking?.close({ animate: false });
|
|
22041
22115
|
asking = openNameDialog({
|
|
22042
22116
|
host: deps.overlays,
|
|
22043
22117
|
t: t(),
|
|
@@ -22189,15 +22263,15 @@ function mountLayoutDialogs(deps) {
|
|
|
22189
22263
|
render();
|
|
22190
22264
|
redraw = render;
|
|
22191
22265
|
closeBrowser = catalog.onChange(render);
|
|
22192
|
-
browser =
|
|
22193
|
-
close: () => handle.close()
|
|
22194
|
-
};
|
|
22266
|
+
browser = handle;
|
|
22195
22267
|
void catalog.refresh().catch(() => {
|
|
22196
22268
|
deps.notify("error", t()("layouts.errList"));
|
|
22197
22269
|
});
|
|
22198
22270
|
},
|
|
22199
22271
|
initialFocus: (box) => box.querySelector(".qc-layouts-search-input"),
|
|
22200
|
-
|
|
22272
|
+
// The dialog lets go of the catalog and its slot as it begins to close, so a Layouts dialog
|
|
22273
|
+
// asked for during the exit opens at once and the leaving one never unsubscribes it.
|
|
22274
|
+
onClosing: () => {
|
|
22201
22275
|
closeBrowser();
|
|
22202
22276
|
browser = null;
|
|
22203
22277
|
}
|
|
@@ -22228,8 +22302,8 @@ function mountLayoutDialogs(deps) {
|
|
|
22228
22302
|
destroy() {
|
|
22229
22303
|
raising.clear();
|
|
22230
22304
|
offLayout();
|
|
22231
|
-
browser?.close();
|
|
22232
|
-
asking?.close();
|
|
22305
|
+
browser?.close({ animate: false });
|
|
22306
|
+
asking?.close({ animate: false });
|
|
22233
22307
|
}
|
|
22234
22308
|
};
|
|
22235
22309
|
}
|
|
@@ -22294,7 +22368,7 @@ function arrangementGlyph(code, icons) {
|
|
|
22294
22368
|
}
|
|
22295
22369
|
var SYNC_ROWS = [
|
|
22296
22370
|
{ key: "symbol", label: "layouts.syncSymbol", tip: "layouts.syncSymbolTip", toggle: "layouts.syncSymbolToggle" },
|
|
22297
|
-
{ key: "
|
|
22371
|
+
{ key: "timeframe", label: "layouts.syncTimeframe", tip: "layouts.syncTimeframeTip", toggle: "layouts.syncTimeframeToggle" },
|
|
22298
22372
|
{ key: "crosshair", label: "layouts.syncCrosshair", tip: "layouts.syncCrosshairTip", toggle: "layouts.syncCrosshairToggle" },
|
|
22299
22373
|
{ key: "time", label: "layouts.syncTime", tip: "layouts.syncTimeTip", toggle: "layouts.syncTimeToggle" },
|
|
22300
22374
|
{ key: "dateRange", label: "layouts.syncDateRange", tip: "layouts.syncDateRangeTip", toggle: "layouts.syncDateRangeToggle" }
|
|
@@ -22594,8 +22668,8 @@ function mountLayoutsMenu(deps) {
|
|
|
22594
22668
|
text: t()("layouts.createNewRow"),
|
|
22595
22669
|
icon: deps.icons.glyph(ICONS.plusThin, { size: 28 }),
|
|
22596
22670
|
disabled: !can("widget.layout.create"),
|
|
22597
|
-
// A new layout by the name it is given: the market and
|
|
22598
|
-
// of the open layout, and the open one left as it was saved.
|
|
22671
|
+
// A new layout by the name it is given: the market and timeframe on screen, nothing
|
|
22672
|
+
// else of the open layout, and the open one left as it was saved.
|
|
22599
22673
|
onSelect: () => askName({
|
|
22600
22674
|
title: "layouts.createTitle",
|
|
22601
22675
|
label: "layouts.newLayoutName",
|
|
@@ -22892,7 +22966,6 @@ function mountSettingsMenu(deps) {
|
|
|
22892
22966
|
host: deps.overlays,
|
|
22893
22967
|
label: t()("settings.menu"),
|
|
22894
22968
|
className: "qc-settings-menu qc-chart-settings-dialog",
|
|
22895
|
-
animated: true,
|
|
22896
22969
|
width: 750,
|
|
22897
22970
|
build(box, handle) {
|
|
22898
22971
|
content = h("div", { class: "qc-dialog-body qc-chart-settings-body" });
|
|
@@ -23760,7 +23833,7 @@ function openDatePicker(deps) {
|
|
|
23760
23833
|
function speedWords(t, speed) {
|
|
23761
23834
|
return speed >= 1 ? t("replay.updatesPerSecond", { count: speed }) : t("replay.oneUpdatePerSeconds", { count: Math.round(1 / speed) });
|
|
23762
23835
|
}
|
|
23763
|
-
function
|
|
23836
|
+
function timeframeWords(t, token) {
|
|
23764
23837
|
const sec = tfSeconds(token);
|
|
23765
23838
|
if (sec === 0) return token;
|
|
23766
23839
|
return timeframeLabel(t, token);
|
|
@@ -23931,36 +24004,36 @@ function mountReplayTransport(deps) {
|
|
|
23931
24004
|
}
|
|
23932
24005
|
}));
|
|
23933
24006
|
};
|
|
23934
|
-
const
|
|
23935
|
-
|
|
23936
|
-
const
|
|
24007
|
+
const timeframeButton = button2({ label: t()("replay.timeframe"), text: "", className: "qc-toolbar-button qc-replay-timeframe", onClick: () => toggleMenu(timeframeButton, openTimeframeMenu) });
|
|
24008
|
+
timeframeButton.setAttribute("aria-haspopup", "menu");
|
|
24009
|
+
const openTimeframeMenu = () => {
|
|
23937
24010
|
const menu2 = hold(openMenu({
|
|
23938
24011
|
host: deps.chrome,
|
|
23939
|
-
anchor:
|
|
23940
|
-
label: t()("replay.
|
|
24012
|
+
anchor: timeframeButton,
|
|
24013
|
+
label: t()("replay.timeframe"),
|
|
23941
24014
|
role: "dialog",
|
|
23942
24015
|
className: "qc-replay-menu",
|
|
23943
|
-
width: FLYOUT_WIDTH.
|
|
24016
|
+
width: FLYOUT_WIDTH.replayTimeframe,
|
|
23944
24017
|
placement: "up",
|
|
23945
24018
|
onClose: () => open2.delete(menu2),
|
|
23946
24019
|
build(body, menu3) {
|
|
23947
|
-
const heading = menuHeading(t()("replay.
|
|
23948
|
-
heading.title = t()("replay.
|
|
24020
|
+
const heading = menuHeading(t()("replay.timeframe"));
|
|
24021
|
+
heading.title = t()("replay.timeframeHelp");
|
|
23949
24022
|
body.appendChild(heading);
|
|
23950
|
-
const current = handle.replay.
|
|
24023
|
+
const current = handle.replay.timeframe();
|
|
23951
24024
|
const rows2 = [];
|
|
23952
24025
|
let previousUnit = null;
|
|
23953
|
-
for (const token of handle.replay.
|
|
24026
|
+
for (const token of handle.replay.subTimeframes()) {
|
|
23954
24027
|
const unit = timeframeGroupUnit(parseTimeframe(token)?.unit ?? "d");
|
|
23955
24028
|
if (previousUnit !== null && unit !== previousUnit) body.appendChild(menuSeparator());
|
|
23956
24029
|
previousUnit = unit;
|
|
23957
24030
|
const row2 = menuItem({
|
|
23958
|
-
text:
|
|
24031
|
+
text: timeframeWords(t(), token),
|
|
23959
24032
|
role: "menuitemradio",
|
|
23960
24033
|
checked: current === token,
|
|
23961
24034
|
onSelect: () => {
|
|
23962
24035
|
menu3.close();
|
|
23963
|
-
commands.execute("chart.replay.
|
|
24036
|
+
commands.execute("chart.replay.setTimeframe", token);
|
|
23964
24037
|
}
|
|
23965
24038
|
});
|
|
23966
24039
|
rows2.push({ token, row: row2 });
|
|
@@ -23969,12 +24042,12 @@ function mountReplayTransport(deps) {
|
|
|
23969
24042
|
body.appendChild(menuSeparator());
|
|
23970
24043
|
body.appendChild(
|
|
23971
24044
|
switchRow({
|
|
23972
|
-
label: t()("replay.
|
|
24045
|
+
label: t()("replay.autoSelectTimeframe"),
|
|
23973
24046
|
checked: current === "auto",
|
|
23974
24047
|
onChange: (on) => {
|
|
23975
|
-
const first = handle.replay.
|
|
23976
|
-
commands.execute("chart.replay.
|
|
23977
|
-
const now = handle.replay.
|
|
24048
|
+
const first = handle.replay.subTimeframes()[0];
|
|
24049
|
+
commands.execute("chart.replay.setTimeframe", on ? "auto" : first ?? "auto");
|
|
24050
|
+
const now = handle.replay.timeframe();
|
|
23978
24051
|
for (const entry of rows2) entry.row.setAttribute("aria-checked", String(now === entry.token));
|
|
23979
24052
|
}
|
|
23980
24053
|
})
|
|
@@ -23994,7 +24067,7 @@ function mountReplayTransport(deps) {
|
|
|
23994
24067
|
playPause,
|
|
23995
24068
|
stepForward,
|
|
23996
24069
|
speedButton,
|
|
23997
|
-
|
|
24070
|
+
timeframeButton,
|
|
23998
24071
|
rule(),
|
|
23999
24072
|
goLive
|
|
24000
24073
|
);
|
|
@@ -24010,19 +24083,19 @@ function mountReplayTransport(deps) {
|
|
|
24010
24083
|
stepForward.hidden = !shown2("chart.replay.stepForward");
|
|
24011
24084
|
goLive.hidden = !shown2("chart.replay.goLive");
|
|
24012
24085
|
speedButton.hidden = !shown2("chart.replay.setSpeed");
|
|
24013
|
-
|
|
24086
|
+
timeframeButton.hidden = !shown2("chart.replay.setTimeframe");
|
|
24014
24087
|
exit.hidden = !shown2("chart.replay.exit");
|
|
24015
24088
|
setDisabled(stepBack, !commands.available("chart.replay.stepBack") || state.cursor <= 2);
|
|
24016
24089
|
setDisabled(playPause, !commands.available(playing ? "chart.replay.pause" : "chart.replay.play") || !playing && atLive);
|
|
24017
24090
|
setDisabled(stepForward, !commands.available("chart.replay.stepForward") || atLive);
|
|
24018
24091
|
setDisabled(goLive, !commands.available("chart.replay.goLive") || atLive);
|
|
24019
24092
|
setDisabled(speedButton, !commands.available("chart.replay.setSpeed"));
|
|
24020
|
-
const
|
|
24021
|
-
const noGrain =
|
|
24022
|
-
setDisabled(
|
|
24093
|
+
const grains = handle.replay.subTimeframes();
|
|
24094
|
+
const noGrain = grains.length === 0;
|
|
24095
|
+
setDisabled(timeframeButton, noGrain || !commands.available("chart.replay.setTimeframe"));
|
|
24023
24096
|
retext(speedButton, t()("replay.speed"), `${state.speed}x`);
|
|
24024
|
-
const resolved = handle.replay.
|
|
24025
|
-
retext(
|
|
24097
|
+
const resolved = handle.replay.resolvedTimeframe();
|
|
24098
|
+
retext(timeframeButton, noGrain ? t()("replay.timeframeNone") : t()("replay.timeframe"), noGrain ? t()("replay.auto") : timeframeChipLabel(resolved));
|
|
24026
24099
|
setDisabled(exit, !commands.available("chart.replay.exit"));
|
|
24027
24100
|
reconcilePicking();
|
|
24028
24101
|
bar.setAttribute("aria-label", t()("chrome.replay"));
|
|
@@ -24053,7 +24126,7 @@ function mountReplayTransport(deps) {
|
|
|
24053
24126
|
if (destroyed) return;
|
|
24054
24127
|
destroyed = true;
|
|
24055
24128
|
releasePicking();
|
|
24056
|
-
for (const handle2 of [...open2]) handle2.close();
|
|
24129
|
+
for (const handle2 of [...open2]) handle2.close({ animate });
|
|
24057
24130
|
offStrings();
|
|
24058
24131
|
const view = bar.ownerDocument.defaultView;
|
|
24059
24132
|
const exitMs = animate && bar.isConnected ? motionDurationMs(bar, "motion.durationBase") : 0;
|
|
@@ -24158,7 +24231,7 @@ function mountChrome(deps) {
|
|
|
24158
24231
|
indicatorDialog = openIndicatorPicker({ ...ctx, storage: deps.storage, indicatorPicker: deps.indicatorPicker, initialCollection });
|
|
24159
24232
|
};
|
|
24160
24233
|
disposers.push(() => {
|
|
24161
|
-
indicatorDialog?.close();
|
|
24234
|
+
indicatorDialog?.close({ animate: false });
|
|
24162
24235
|
deps.doors.showIndicatorPicker = () => void 0;
|
|
24163
24236
|
});
|
|
24164
24237
|
deps.doors.openSearch = (request) => {
|
|
@@ -24213,7 +24286,8 @@ function mountChrome(deps) {
|
|
|
24213
24286
|
widget.theme.onChange((theme, mode) => paintThemeRoot(surface, mode, theme)),
|
|
24214
24287
|
i18n.onChange(() => surface.setAttribute("dir", readingDirection(i18n))),
|
|
24215
24288
|
() => surface.remove(),
|
|
24216
|
-
// A flyout still open in the layer closes with the
|
|
24289
|
+
// A flyout still open in the layer closes with the drawing toolbar, taking its document
|
|
24290
|
+
// listeners.
|
|
24217
24291
|
() => closeOverlays(deps.layer),
|
|
24218
24292
|
releasePanelHost
|
|
24219
24293
|
);
|
|
@@ -24404,7 +24478,7 @@ function resolveMarkPainters(hooks) {
|
|
|
24404
24478
|
return {
|
|
24405
24479
|
symbol: hooks.symbolMark ?? null,
|
|
24406
24480
|
venue: hooks.venueMark ?? null,
|
|
24407
|
-
|
|
24481
|
+
dataSource: hooks.dataSourceMark ?? null
|
|
24408
24482
|
};
|
|
24409
24483
|
}
|
|
24410
24484
|
|
|
@@ -24857,7 +24931,7 @@ function createChart(options) {
|
|
|
24857
24931
|
i18n,
|
|
24858
24932
|
features,
|
|
24859
24933
|
ui,
|
|
24860
|
-
// What the chrome keeps is the viewer's own choices about it (favorite and custom
|
|
24934
|
+
// What the chrome keeps is the viewer's own choices about it (favorite and custom timeframes,
|
|
24861
24935
|
// starred layouts and indicators, how the layouts sort), which change no layout, so it reads and
|
|
24862
24936
|
// writes the host's store directly rather than through the save-needed funnel.
|
|
24863
24937
|
storage: backing,
|
|
@@ -25182,7 +25256,7 @@ export {
|
|
|
25182
25256
|
attachCompare,
|
|
25183
25257
|
attachDrawings,
|
|
25184
25258
|
attachIndicators,
|
|
25185
|
-
|
|
25259
|
+
autoTimeframeFor,
|
|
25186
25260
|
buildManifestPlots,
|
|
25187
25261
|
canvasTheme,
|
|
25188
25262
|
canvasToBlob,
|
|
@@ -25269,7 +25343,7 @@ export {
|
|
|
25269
25343
|
sessionTimeline,
|
|
25270
25344
|
spreadExpression,
|
|
25271
25345
|
spreadSearchQuery,
|
|
25272
|
-
|
|
25346
|
+
subTimeframesFor,
|
|
25273
25347
|
subsessionBarFilter,
|
|
25274
25348
|
symbolNames,
|
|
25275
25349
|
tfSeconds,
|