@trdrs/quickcharts 1.1.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +4 -0
- package/README.md +2 -0
- package/dist/feature-manifest.json +1 -1
- package/dist/index.d.ts +1958 -1945
- package/dist/index.js +336 -110
- package/dist/index.js.map +1 -1
- package/dist/quickcharts.css +407 -4
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -8949,24 +8949,52 @@ var OPERATOR_GLYPHS = {
|
|
|
8949
8949
|
|
|
8950
8950
|
// src/ui/chrome/dialog.ts
|
|
8951
8951
|
function openDialog(options) {
|
|
8952
|
-
const scrim = h("div", { class:
|
|
8952
|
+
const scrim = h("div", { class: `qc-scrim qc-dialog-scrim${options.scrimClassName ? ` ${options.scrimClassName}` : ""}` });
|
|
8953
8953
|
const box = h("div", { class: `qc-overlay qc-dialog${options.className ? ` ${options.className}` : ""}`, role: options.ariaRole ?? "dialog", "aria-modal": "true", "aria-label": options.label, "data-role": options.role });
|
|
8954
8954
|
if (options.width) box.style.width = `${options.width}px`;
|
|
8955
8955
|
stopPointer(box);
|
|
8956
8956
|
scrim.appendChild(box);
|
|
8957
|
+
if (options.exitMs) scrim.dataset.state = "opening";
|
|
8957
8958
|
let isOpen = true;
|
|
8958
8959
|
const previouslyFocused = document.activeElement;
|
|
8959
8960
|
const escape = pushEscapeOwner();
|
|
8960
8961
|
let untrack = () => void 0;
|
|
8961
|
-
|
|
8962
|
-
|
|
8962
|
+
let removed = false;
|
|
8963
|
+
let exitTimer = null;
|
|
8964
|
+
const remove = () => {
|
|
8965
|
+
if (removed) return;
|
|
8966
|
+
removed = true;
|
|
8967
|
+
if (exitTimer !== null) clearTimeout(exitTimer);
|
|
8968
|
+
scrim.remove();
|
|
8969
|
+
options.onClose?.();
|
|
8970
|
+
};
|
|
8971
|
+
const close = (closeOptions) => {
|
|
8972
|
+
if (!isOpen) {
|
|
8973
|
+
if (closeOptions?.animate === false) remove();
|
|
8974
|
+
return;
|
|
8975
|
+
}
|
|
8963
8976
|
isOpen = false;
|
|
8977
|
+
options.onClosing?.();
|
|
8964
8978
|
escape.release();
|
|
8965
8979
|
untrack();
|
|
8966
8980
|
document.removeEventListener("keydown", onKey, true);
|
|
8967
|
-
scrim.remove();
|
|
8968
|
-
options.onClose?.();
|
|
8969
8981
|
previouslyFocused?.focus?.();
|
|
8982
|
+
const reduceMotion = document.defaultView?.matchMedia?.("(prefers-reduced-motion: reduce)").matches ?? false;
|
|
8983
|
+
if (closeOptions?.animate === false || !options.exitMs || reduceMotion) {
|
|
8984
|
+
remove();
|
|
8985
|
+
return;
|
|
8986
|
+
}
|
|
8987
|
+
scrim.dataset.state = "closing";
|
|
8988
|
+
scrim.style.pointerEvents = "none";
|
|
8989
|
+
box.inert = true;
|
|
8990
|
+
box.removeAttribute("role");
|
|
8991
|
+
box.removeAttribute("aria-modal");
|
|
8992
|
+
box.setAttribute("aria-hidden", "true");
|
|
8993
|
+
const onAnimationEnd = (event) => {
|
|
8994
|
+
if (event.target === box) remove();
|
|
8995
|
+
};
|
|
8996
|
+
box.addEventListener("animationend", onAnimationEnd, { once: true });
|
|
8997
|
+
exitTimer = setTimeout(remove, options.exitMs + 50);
|
|
8970
8998
|
};
|
|
8971
8999
|
const onKey = (event) => {
|
|
8972
9000
|
if (event.key === "Escape" && ownsEscape(escape.token)) {
|
|
@@ -8998,6 +9026,10 @@ function openDialog(options) {
|
|
|
8998
9026
|
const handle = { element: box, close, open: () => isOpen };
|
|
8999
9027
|
options.build(box, handle);
|
|
9000
9028
|
(layerFor(options.host) ?? options.host).appendChild(scrim);
|
|
9029
|
+
if (options.exitMs) {
|
|
9030
|
+
scrim.getBoundingClientRect();
|
|
9031
|
+
scrim.dataset.state = "open";
|
|
9032
|
+
}
|
|
9001
9033
|
untrack = trackOverlay(options.host, close);
|
|
9002
9034
|
document.addEventListener("keydown", onKey, true);
|
|
9003
9035
|
const target = options.initialFocus?.(box) ?? focusables(box)[0] ?? null;
|
|
@@ -14974,6 +15006,7 @@ function mountChartLegend(container, strings, controls) {
|
|
|
14974
15006
|
update(next) {
|
|
14975
15007
|
chip = next;
|
|
14976
15008
|
row2.dataset.qcHidden = String(chip.hidden);
|
|
15009
|
+
row2.title = chip.description ?? "";
|
|
14977
15010
|
rowMark.element.hidden = !chip.mark;
|
|
14978
15011
|
if (!chip.mark) {
|
|
14979
15012
|
dropMark?.();
|
|
@@ -15008,7 +15041,7 @@ function mountChartLegend(container, strings, controls) {
|
|
|
15008
15041
|
if (chip.tone && !chip.hidden && !chip.note) value.dataset.qcTone = chip.tone;
|
|
15009
15042
|
else delete value.dataset.qcTone;
|
|
15010
15043
|
gear.hidden = !chip.hasInputs || !controls.onSettings;
|
|
15011
|
-
nameButton(gear, strings.t("legend.indicatorSettings"));
|
|
15044
|
+
nameButton(gear, chip.settingsLabel ?? strings.t("legend.indicatorSettings"));
|
|
15012
15045
|
collapse.hidden = !chip.pane || !controls.onPaneOp || chip.hidden;
|
|
15013
15046
|
collapse.replaceChildren(controls.icons.glyph(chip.collapsed ? ICONS.paneRestore : ICONS.paneCollapse, { size: 18 }));
|
|
15014
15047
|
nameButton(collapse, strings.t(chip.collapsed ? "legend.restorePane" : "legend.collapsePane"));
|
|
@@ -15021,6 +15054,7 @@ function mountChartLegend(container, strings, controls) {
|
|
|
15021
15054
|
eye.dataset.hidden = eyeState;
|
|
15022
15055
|
}
|
|
15023
15056
|
nameButton(eye, strings.t(chip.hidden ? "legend.showIndicator" : "legend.hideIndicator"));
|
|
15057
|
+
eye.hidden = chip.hideable === false;
|
|
15024
15058
|
remove.hidden = !chip.removable || !controls.onRemove;
|
|
15025
15059
|
nameButton(remove, chip.removeLabel ?? strings.t("legend.removeCompare"));
|
|
15026
15060
|
},
|
|
@@ -15566,6 +15600,7 @@ function openMarketStatus(anchor, deps) {
|
|
|
15566
15600
|
|
|
15567
15601
|
// src/widget/legend.ts
|
|
15568
15602
|
var COMPARE_ROW_PREFIX = "cmp:";
|
|
15603
|
+
var HOST_ROW_PREFIX = "host:";
|
|
15569
15604
|
function barIndexAt(bars, time) {
|
|
15570
15605
|
if (bars.length === 0) return -1;
|
|
15571
15606
|
if (time === null) return bars.length - 1;
|
|
@@ -15594,10 +15629,12 @@ function attachLegendPlane(deps) {
|
|
|
15594
15629
|
push: () => void 0,
|
|
15595
15630
|
setHeader: () => void 0,
|
|
15596
15631
|
setDot: () => void 0,
|
|
15632
|
+
setHostRows: () => void 0,
|
|
15597
15633
|
destroy: () => void 0
|
|
15598
15634
|
};
|
|
15599
15635
|
}
|
|
15600
15636
|
let destroyed = false;
|
|
15637
|
+
const hostRows = /* @__PURE__ */ new Map();
|
|
15601
15638
|
const paneRemembered = /* @__PURE__ */ new Map();
|
|
15602
15639
|
const paneKeys = () => {
|
|
15603
15640
|
const keys = { 0: "main" };
|
|
@@ -15653,6 +15690,11 @@ function attachLegendPlane(deps) {
|
|
|
15653
15690
|
}
|
|
15654
15691
|
} : {},
|
|
15655
15692
|
onSettings: (id, rect) => {
|
|
15693
|
+
const host2 = hostRows.get(id);
|
|
15694
|
+
if (host2) {
|
|
15695
|
+
host2.onSettings?.();
|
|
15696
|
+
return;
|
|
15697
|
+
}
|
|
15656
15698
|
if (deps.openIndicatorSettings(id)) return;
|
|
15657
15699
|
const inst = deps.indicators.list().find((i) => i.id === id);
|
|
15658
15700
|
if (!inst) return;
|
|
@@ -15723,7 +15765,21 @@ function attachLegendPlane(deps) {
|
|
|
15723
15765
|
const remembered = paneRemembered.get(`study:${row2.id}`);
|
|
15724
15766
|
return { ...row2, paneIndex, maximized: remembered !== void 0 && !row2.collapsed && (deps.chart.panes()[paneIndex]?.getHeight() ?? 0) > remembered };
|
|
15725
15767
|
}),
|
|
15726
|
-
...(deps.compare?.chips(hovered) ?? []).map((row2) => ({ ...row2, paneIndex: deps.compare.handle.paneIndexOf(row2.id.slice(COMPARE_ROW_PREFIX.length)) ?? 0 }))
|
|
15768
|
+
...(deps.compare?.chips(hovered) ?? []).map((row2) => ({ ...row2, paneIndex: deps.compare.handle.paneIndexOf(row2.id.slice(COMPARE_ROW_PREFIX.length)) ?? 0 })),
|
|
15769
|
+
...[...hostRows].map(([id, row2]) => ({
|
|
15770
|
+
id,
|
|
15771
|
+
title: row2.title,
|
|
15772
|
+
inputs: row2.inputs,
|
|
15773
|
+
value: null,
|
|
15774
|
+
note: row2.status,
|
|
15775
|
+
description: row2.description,
|
|
15776
|
+
settingsLabel: row2.settingsLabel,
|
|
15777
|
+
hidden: false,
|
|
15778
|
+
hideable: false,
|
|
15779
|
+
removable: false,
|
|
15780
|
+
hasInputs: !!row2.onSettings,
|
|
15781
|
+
paneIndex: 0
|
|
15782
|
+
}))
|
|
15727
15783
|
]);
|
|
15728
15784
|
const tops = {};
|
|
15729
15785
|
let top = 0;
|
|
@@ -15766,6 +15822,12 @@ function attachLegendPlane(deps) {
|
|
|
15766
15822
|
};
|
|
15767
15823
|
deps.chart.subscribeCrosshairMove(onCrosshair);
|
|
15768
15824
|
return {
|
|
15825
|
+
setHostRows(rows2) {
|
|
15826
|
+
if (destroyed) return;
|
|
15827
|
+
hostRows.clear();
|
|
15828
|
+
for (const row2 of rows2) hostRows.set(`${HOST_ROW_PREFIX}${row2.id}`, { ...row2 });
|
|
15829
|
+
paint();
|
|
15830
|
+
},
|
|
15769
15831
|
push() {
|
|
15770
15832
|
paint();
|
|
15771
15833
|
},
|
|
@@ -15791,6 +15853,7 @@ function attachLegendPlane(deps) {
|
|
|
15791
15853
|
},
|
|
15792
15854
|
destroy() {
|
|
15793
15855
|
destroyed = true;
|
|
15856
|
+
hostRows.clear();
|
|
15794
15857
|
resize?.disconnect();
|
|
15795
15858
|
observed.clear();
|
|
15796
15859
|
deps.chart.unsubscribeCrosshairMove(onCrosshair);
|
|
@@ -16929,7 +16992,13 @@ function registerChartCommands(deps) {
|
|
|
16929
16992
|
available: () => features.history && handle.history.canRedo(),
|
|
16930
16993
|
execute: () => handle.history.redo()
|
|
16931
16994
|
});
|
|
16932
|
-
add({
|
|
16995
|
+
add({
|
|
16996
|
+
id: "chart.indicators.open",
|
|
16997
|
+
scope: "chart",
|
|
16998
|
+
label: "chrome.indicators",
|
|
16999
|
+
available: () => ui.indicatorPicker,
|
|
17000
|
+
execute: (arg) => deps.indicatorsOpen(arg && typeof arg === "object" && "collection" in arg && typeof arg.collection === "string" ? arg.collection : void 0)
|
|
17001
|
+
});
|
|
16933
17002
|
const isInstance = (arg) => !!arg && typeof arg === "object" && typeof arg.id === "string" && typeof arg.definition === "object";
|
|
16934
17003
|
add({
|
|
16935
17004
|
id: "chart.indicators.add",
|
|
@@ -18726,7 +18795,7 @@ function createChartInstance(deps) {
|
|
|
18726
18795
|
level: () => menuLevel,
|
|
18727
18796
|
formatter: () => symbolFormatter,
|
|
18728
18797
|
compareOpen: (mode, changeFrom) => compare?.openDialog(mode, changeFrom),
|
|
18729
|
-
indicatorsOpen: () => deps.doors.showIndicatorPicker(),
|
|
18798
|
+
indicatorsOpen: (collection) => deps.doors.showIndicatorPicker(collection),
|
|
18730
18799
|
symbolSearchOpen: () => deps.doors.openSearch({ mode: "search", chart: handle }),
|
|
18731
18800
|
drawingVerbs: () => drawings2.verbs
|
|
18732
18801
|
});
|
|
@@ -18773,6 +18842,7 @@ function createChartInstance(deps) {
|
|
|
18773
18842
|
}
|
|
18774
18843
|
return {
|
|
18775
18844
|
handle,
|
|
18845
|
+
setLegendRows: (rows2) => legend2.setHostRows(rows2),
|
|
18776
18846
|
screenshot: () => chart.takeScreenshot(),
|
|
18777
18847
|
repaintTheme() {
|
|
18778
18848
|
applyLook();
|
|
@@ -20596,7 +20666,7 @@ function openIndicatorPicker(deps) {
|
|
|
20596
20666
|
} catch {
|
|
20597
20667
|
}
|
|
20598
20668
|
let query = "";
|
|
20599
|
-
let collection = "builtin";
|
|
20669
|
+
let collection = deps.initialCollection === "favorites" || collections?.some((row2) => row2.id === deps.initialCollection) ? deps.initialCollection : "builtin";
|
|
20600
20670
|
let generation = 0;
|
|
20601
20671
|
let controller = null;
|
|
20602
20672
|
let busy = false;
|
|
@@ -21165,6 +21235,8 @@ function openSearchDialog(deps) {
|
|
|
21165
21235
|
host: deps.host,
|
|
21166
21236
|
label: title,
|
|
21167
21237
|
className: `qc-search-dialog qc-search-dialog--${mode}`,
|
|
21238
|
+
scrimClassName: "qc-search-dialog-scrim",
|
|
21239
|
+
exitMs: 140,
|
|
21168
21240
|
// The compare family (compare, and a compare row's change-symbol) shares one role for a host
|
|
21169
21241
|
// test to find; a symbol search, with or without a chart behind it, is the other.
|
|
21170
21242
|
role: mode === "compare" || mode === "change-symbol" ? "compare-dialog" : "symbol-search",
|
|
@@ -21944,127 +22016,238 @@ var TOGGLE_ROWS = [
|
|
|
21944
22016
|
{ leaf: "grid", label: "settings.gridLines" },
|
|
21945
22017
|
{ leaf: "sessions", label: "settings.sessionShading" }
|
|
21946
22018
|
];
|
|
22019
|
+
var nextSettingsId = 0;
|
|
21947
22020
|
function mountSettingsMenu(deps) {
|
|
21948
22021
|
const t = () => deps.i18n.t;
|
|
21949
|
-
|
|
22022
|
+
const settingsId = ++nextSettingsId;
|
|
22023
|
+
let dialog = null;
|
|
22024
|
+
let content = null;
|
|
22025
|
+
let activePage = "appearance";
|
|
22026
|
+
let committed = false;
|
|
22027
|
+
let initial = null;
|
|
21950
22028
|
const controls = [];
|
|
21951
22029
|
const retireControls = () => {
|
|
21952
22030
|
for (const control of controls) control.destroy();
|
|
21953
22031
|
controls.length = 0;
|
|
21954
22032
|
};
|
|
21955
|
-
const trigger = button2({
|
|
22033
|
+
const trigger = button2({
|
|
22034
|
+
label: t()("settings.menu"),
|
|
22035
|
+
icon: deps.icons.glyph(ICONS.settings),
|
|
22036
|
+
className: "qc-toolbar-button",
|
|
22037
|
+
onClick: () => {
|
|
22038
|
+
if (dialog) {
|
|
22039
|
+
if (dialog.open()) dialog.close();
|
|
22040
|
+
return;
|
|
22041
|
+
}
|
|
22042
|
+
open2();
|
|
22043
|
+
}
|
|
22044
|
+
});
|
|
21956
22045
|
trigger.setAttribute("aria-haspopup", "dialog");
|
|
21957
22046
|
trigger.setAttribute("aria-expanded", "false");
|
|
21958
22047
|
const apply = (partial) => {
|
|
21959
22048
|
deps.commands.execute("chart.appearance.apply", { appearance: partial });
|
|
21960
22049
|
};
|
|
22050
|
+
const refresh = () => {
|
|
22051
|
+
if (!dialog?.open() || !content) return;
|
|
22052
|
+
retireControls();
|
|
22053
|
+
content.replaceChildren();
|
|
22054
|
+
buildContent(content);
|
|
22055
|
+
};
|
|
22056
|
+
const pageDefinitions = () => [
|
|
22057
|
+
{ id: "appearance", label: t()("settings.sectionAppearance"), icon: STYLE_ICONS.candles },
|
|
22058
|
+
{ id: "display", label: t()("settings.sectionDisplay"), icon: ICONS.template },
|
|
22059
|
+
{ id: "scale", label: t()("settings.sectionScale"), icon: ICONS.ruler },
|
|
22060
|
+
...deps.ui.settingsTheme ? [{ id: "theme", label: t()("settings.sectionTheme"), icon: ICONS.settings }] : []
|
|
22061
|
+
];
|
|
22062
|
+
const showPage = (page, focus = false) => {
|
|
22063
|
+
activePage = page;
|
|
22064
|
+
refresh();
|
|
22065
|
+
if (focus) content?.querySelector(`[data-settings-page="${page}"]`)?.focus();
|
|
22066
|
+
};
|
|
22067
|
+
const buildAppearancePage = (panel) => {
|
|
22068
|
+
const appearance = activeChart(deps).appearance().appearance;
|
|
22069
|
+
const canApply = deps.commands.available("chart.appearance.apply");
|
|
22070
|
+
panel.appendChild(menuHeading(t()("settings.sectionAppearance")));
|
|
22071
|
+
for (const row2 of COLOR_ROWS) {
|
|
22072
|
+
const label2 = t()(row2.label);
|
|
22073
|
+
const value = appearance[row2.leaf];
|
|
22074
|
+
const control = createColorControl(t(), {
|
|
22075
|
+
label: label2,
|
|
22076
|
+
value,
|
|
22077
|
+
opacity: readColor(value)?.alpha ?? 1,
|
|
22078
|
+
disabled: !canApply,
|
|
22079
|
+
closeOnPick: true,
|
|
22080
|
+
onPick: (color2) => {
|
|
22081
|
+
control.update(color2);
|
|
22082
|
+
apply({ [row2.leaf]: color2 });
|
|
22083
|
+
},
|
|
22084
|
+
openPanel: (anchor, palette, onClosed) => openInlinePanel(anchor, palette, anchor.closest(".qc-settings-row") ?? anchor, onClosed)
|
|
22085
|
+
});
|
|
22086
|
+
controls.push(control);
|
|
22087
|
+
panel.appendChild(fieldRow(label2, control.element, { className: "qc-settings-row" }));
|
|
22088
|
+
}
|
|
22089
|
+
};
|
|
22090
|
+
const buildDisplayPage = (panel) => {
|
|
22091
|
+
const appearance = activeChart(deps).appearance().appearance;
|
|
22092
|
+
const canApply = deps.commands.available("chart.appearance.apply");
|
|
22093
|
+
panel.appendChild(menuHeading(t()("settings.sectionDisplay")));
|
|
22094
|
+
for (const row2 of TOGGLE_ROWS) {
|
|
22095
|
+
panel.appendChild(switchRow({ label: t()(row2.label), checked: appearance[row2.leaf], disabled: !canApply, onChange: (on) => apply({ [row2.leaf]: on }) }));
|
|
22096
|
+
}
|
|
22097
|
+
};
|
|
22098
|
+
const choice = (label2, checked, disabled, onSelect) => {
|
|
22099
|
+
const row2 = h("button", {
|
|
22100
|
+
type: "button",
|
|
22101
|
+
class: "qc-chart-settings-choice",
|
|
22102
|
+
role: "radio",
|
|
22103
|
+
"aria-checked": String(checked),
|
|
22104
|
+
disabled
|
|
22105
|
+
}, h("span", { class: "qc-chart-settings-radio", "aria-hidden": "true" }), h("span", { class: "qc-chart-settings-choice-label" }, label2));
|
|
22106
|
+
row2.addEventListener("click", onSelect);
|
|
22107
|
+
return row2;
|
|
22108
|
+
};
|
|
22109
|
+
const buildScalePage = (panel) => {
|
|
22110
|
+
const scale = activeChart(deps).scaleMode();
|
|
22111
|
+
panel.appendChild(menuHeading(t()("settings.sectionScale")));
|
|
22112
|
+
const group = h("div", { class: "qc-chart-settings-choices", role: "radiogroup", "aria-label": t()("settings.sectionScale") });
|
|
22113
|
+
for (const option of SCALE_MODE_OPTIONS) {
|
|
22114
|
+
const id = `chart.scale.${option.id}`;
|
|
22115
|
+
group.appendChild(choice(commandLabel(deps, id), option.id === scale, option.id !== scale && !deps.commands.available(id), () => {
|
|
22116
|
+
deps.commands.execute(id);
|
|
22117
|
+
refresh();
|
|
22118
|
+
}));
|
|
22119
|
+
}
|
|
22120
|
+
panel.appendChild(group);
|
|
22121
|
+
};
|
|
22122
|
+
const buildThemePage = (panel) => {
|
|
22123
|
+
const mode = deps.widget.theme.mode();
|
|
22124
|
+
panel.appendChild(menuHeading(t()("settings.sectionTheme")));
|
|
22125
|
+
const group = h("div", { class: "qc-chart-settings-choices", role: "radiogroup", "aria-label": t()("settings.sectionTheme") });
|
|
22126
|
+
for (const themeMode of THEME_MODES) {
|
|
22127
|
+
const id = `widget.theme.${themeMode}`;
|
|
22128
|
+
group.appendChild(choice(commandLabel(deps, id), themeMode === mode, themeMode !== mode && !deps.commands.available(id), () => {
|
|
22129
|
+
deps.commands.execute(id);
|
|
22130
|
+
refresh();
|
|
22131
|
+
}));
|
|
22132
|
+
}
|
|
22133
|
+
panel.appendChild(group);
|
|
22134
|
+
};
|
|
22135
|
+
const buildContent = (body) => {
|
|
22136
|
+
const pages = pageDefinitions();
|
|
22137
|
+
if (!pages.some((page) => page.id === activePage)) activePage = "appearance";
|
|
22138
|
+
const panelId = `qc-chart-settings-panel-${settingsId}`;
|
|
22139
|
+
const selectedTabId = `qc-chart-settings-tab-${settingsId}-${activePage}`;
|
|
22140
|
+
const nav = h("div", { class: "qc-chart-settings-nav", role: "tablist", "aria-orientation": "vertical", "aria-label": t()("settings.menu") });
|
|
22141
|
+
const panel = h("section", { id: panelId, class: "qc-chart-settings-panel", role: "tabpanel", "aria-labelledby": selectedTabId, tabindex: "0" });
|
|
22142
|
+
pages.forEach((page, index) => {
|
|
22143
|
+
const selected = page.id === activePage;
|
|
22144
|
+
const tab = h("button", {
|
|
22145
|
+
id: `qc-chart-settings-tab-${settingsId}-${page.id}`,
|
|
22146
|
+
type: "button",
|
|
22147
|
+
class: "qc-chart-settings-nav-item",
|
|
22148
|
+
role: "tab",
|
|
22149
|
+
"aria-selected": String(selected),
|
|
22150
|
+
"aria-controls": panelId,
|
|
22151
|
+
tabindex: selected ? "0" : "-1",
|
|
22152
|
+
"data-settings-page": page.id
|
|
22153
|
+
}, deps.icons.glyph(page.icon, { size: 20 }), h("span", {}, page.label));
|
|
22154
|
+
tab.addEventListener("click", () => showPage(page.id, true));
|
|
22155
|
+
tab.addEventListener("keydown", (event) => {
|
|
22156
|
+
let next = index;
|
|
22157
|
+
if (event.key === "ArrowDown" || event.key === "ArrowRight") next = (index + 1) % pages.length;
|
|
22158
|
+
else if (event.key === "ArrowUp" || event.key === "ArrowLeft") next = (index - 1 + pages.length) % pages.length;
|
|
22159
|
+
else if (event.key === "Home") next = 0;
|
|
22160
|
+
else if (event.key === "End") next = pages.length - 1;
|
|
22161
|
+
else return;
|
|
22162
|
+
event.preventDefault();
|
|
22163
|
+
showPage(pages[next].id, true);
|
|
22164
|
+
});
|
|
22165
|
+
nav.appendChild(tab);
|
|
22166
|
+
});
|
|
22167
|
+
if (activePage === "appearance") buildAppearancePage(panel);
|
|
22168
|
+
else if (activePage === "display") buildDisplayPage(panel);
|
|
22169
|
+
else if (activePage === "scale") buildScalePage(panel);
|
|
22170
|
+
else buildThemePage(panel);
|
|
22171
|
+
body.append(nav, panel);
|
|
22172
|
+
};
|
|
22173
|
+
const restoreInitialState = () => {
|
|
22174
|
+
if (!initial) return;
|
|
22175
|
+
const chart = activeChart(deps);
|
|
22176
|
+
deps.commands.execute("chart.appearance.apply", { appearance: initial.appearance });
|
|
22177
|
+
if (chart.scaleMode() !== initial.scale) deps.commands.execute(`chart.scale.${initial.scale}`);
|
|
22178
|
+
if (deps.ui.settingsTheme && deps.widget.theme.mode() !== initial.theme) deps.commands.execute(`widget.theme.${initial.theme}`);
|
|
22179
|
+
};
|
|
21961
22180
|
const open2 = () => {
|
|
21962
|
-
|
|
22181
|
+
const chart = activeChart(deps);
|
|
22182
|
+
activePage = "appearance";
|
|
22183
|
+
committed = false;
|
|
22184
|
+
initial = {
|
|
22185
|
+
appearance: { ...chart.appearance().appearance },
|
|
22186
|
+
scale: chart.scaleMode(),
|
|
22187
|
+
theme: deps.widget.theme.mode()
|
|
22188
|
+
};
|
|
22189
|
+
dialog = openDialog({
|
|
21963
22190
|
host: deps.overlays,
|
|
21964
|
-
anchor: trigger,
|
|
21965
22191
|
label: t()("settings.menu"),
|
|
21966
|
-
|
|
21967
|
-
|
|
21968
|
-
|
|
21969
|
-
|
|
21970
|
-
build(
|
|
21971
|
-
|
|
21972
|
-
const
|
|
21973
|
-
const
|
|
21974
|
-
|
|
21975
|
-
|
|
21976
|
-
|
|
21977
|
-
|
|
21978
|
-
|
|
21979
|
-
|
|
21980
|
-
|
|
21981
|
-
|
|
21982
|
-
|
|
21983
|
-
|
|
21984
|
-
|
|
21985
|
-
|
|
21986
|
-
|
|
21987
|
-
|
|
21988
|
-
|
|
21989
|
-
|
|
21990
|
-
|
|
21991
|
-
|
|
21992
|
-
|
|
21993
|
-
});
|
|
21994
|
-
controls.push(control);
|
|
21995
|
-
body.appendChild(fieldRow(label2, control.element, { className: "qc-settings-row" }));
|
|
21996
|
-
}
|
|
21997
|
-
body.appendChild(menuSeparator());
|
|
21998
|
-
body.appendChild(menuHeading(t()("settings.sectionDisplay")));
|
|
21999
|
-
for (const row2 of TOGGLE_ROWS) {
|
|
22000
|
-
body.appendChild(switchRow({ label: t()(row2.label), checked: appearance[row2.leaf], disabled: !canApply, onChange: (on) => apply({ [row2.leaf]: on }) }));
|
|
22001
|
-
}
|
|
22002
|
-
body.appendChild(menuSeparator());
|
|
22003
|
-
body.appendChild(menuHeading(t()("settings.sectionScale")));
|
|
22004
|
-
const scale = chart.scaleMode();
|
|
22005
|
-
for (const option of SCALE_MODE_OPTIONS) {
|
|
22006
|
-
const id = `chart.scale.${option.id}`;
|
|
22007
|
-
body.appendChild(
|
|
22008
|
-
menuItem({
|
|
22009
|
-
text: commandLabel(deps, id),
|
|
22010
|
-
role: "menuitemradio",
|
|
22011
|
-
checked: option.id === scale,
|
|
22012
|
-
disabled: option.id !== scale && !deps.commands.available(id),
|
|
22013
|
-
onSelect: () => {
|
|
22014
|
-
deps.commands.execute(id);
|
|
22015
|
-
handle.refresh();
|
|
22016
|
-
}
|
|
22017
|
-
})
|
|
22018
|
-
);
|
|
22019
|
-
}
|
|
22020
|
-
body.appendChild(menuSeparator());
|
|
22021
|
-
body.appendChild(
|
|
22022
|
-
menuItem({
|
|
22023
|
-
text: commandLabel(deps, "chart.appearance.reset"),
|
|
22024
|
-
disabled: !deps.commands.available("chart.appearance.reset"),
|
|
22025
|
-
onSelect: () => {
|
|
22026
|
-
deps.commands.execute("chart.appearance.reset");
|
|
22027
|
-
handle.refresh();
|
|
22192
|
+
className: "qc-settings-menu qc-chart-settings-dialog",
|
|
22193
|
+
scrimClassName: "qc-chart-settings-scrim",
|
|
22194
|
+
exitMs: 180,
|
|
22195
|
+
width: 750,
|
|
22196
|
+
build(box, handle) {
|
|
22197
|
+
content = h("div", { class: "qc-dialog-body qc-chart-settings-body" });
|
|
22198
|
+
const footer = h("div", { class: "qc-chart-settings-footer" });
|
|
22199
|
+
const reset = button2({
|
|
22200
|
+
label: commandLabel(deps, "chart.appearance.reset"),
|
|
22201
|
+
text: commandLabel(deps, "chart.appearance.reset"),
|
|
22202
|
+
className: "qc-chart-settings-reset",
|
|
22203
|
+
disabled: !deps.commands.available("chart.appearance.reset"),
|
|
22204
|
+
onClick: () => {
|
|
22205
|
+
deps.commands.execute("chart.appearance.reset");
|
|
22206
|
+
refresh();
|
|
22207
|
+
}
|
|
22208
|
+
});
|
|
22209
|
+
const actions2 = h("div", { class: "qc-chart-settings-actions" });
|
|
22210
|
+
actions2.append(
|
|
22211
|
+
button2({ label: t()("drawing.cancel"), text: t()("drawing.cancel"), className: "qc-chart-settings-cancel", onClick: () => handle.close() }),
|
|
22212
|
+
button2({
|
|
22213
|
+
label: t()("drawing.ok"),
|
|
22214
|
+
text: t()("drawing.ok"),
|
|
22215
|
+
className: "qc-button--primary qc-chart-settings-ok",
|
|
22216
|
+
onClick: () => {
|
|
22217
|
+
committed = true;
|
|
22218
|
+
handle.close();
|
|
22028
22219
|
}
|
|
22029
22220
|
})
|
|
22030
22221
|
);
|
|
22031
|
-
|
|
22032
|
-
|
|
22033
|
-
|
|
22034
|
-
|
|
22035
|
-
|
|
22036
|
-
|
|
22037
|
-
|
|
22038
|
-
|
|
22039
|
-
text: commandLabel(deps, id),
|
|
22040
|
-
role: "menuitemradio",
|
|
22041
|
-
checked: themeMode === mode,
|
|
22042
|
-
disabled: themeMode !== mode && !deps.commands.available(id),
|
|
22043
|
-
onSelect: () => {
|
|
22044
|
-
deps.commands.execute(id);
|
|
22045
|
-
handle.refresh();
|
|
22046
|
-
}
|
|
22047
|
-
})
|
|
22048
|
-
);
|
|
22049
|
-
}
|
|
22050
|
-
}
|
|
22222
|
+
footer.append(reset, actions2);
|
|
22223
|
+
box.append(dialogTitle(t()("drawing.settings"), t()("layouts.close"), () => handle.close(), deps.icons), content, footer);
|
|
22224
|
+
buildContent(content);
|
|
22225
|
+
},
|
|
22226
|
+
initialFocus: (box) => box.querySelector('[role="tab"][aria-selected="true"]'),
|
|
22227
|
+
onClosing: () => {
|
|
22228
|
+
trigger.setAttribute("aria-expanded", "false");
|
|
22229
|
+
if (!committed) restoreInitialState();
|
|
22051
22230
|
},
|
|
22052
22231
|
onClose: () => {
|
|
22053
22232
|
retireControls();
|
|
22054
|
-
|
|
22233
|
+
content = null;
|
|
22234
|
+
dialog = null;
|
|
22235
|
+
initial = null;
|
|
22236
|
+
trigger.setAttribute("aria-expanded", "false");
|
|
22055
22237
|
}
|
|
22056
22238
|
});
|
|
22239
|
+
trigger.setAttribute("aria-expanded", "true");
|
|
22057
22240
|
};
|
|
22058
22241
|
const sync = () => {
|
|
22059
22242
|
name(trigger, t()("settings.menu"));
|
|
22060
|
-
|
|
22243
|
+
refresh();
|
|
22061
22244
|
};
|
|
22062
22245
|
sync();
|
|
22063
22246
|
return {
|
|
22064
22247
|
element: trigger,
|
|
22065
22248
|
sync,
|
|
22066
22249
|
destroy() {
|
|
22067
|
-
|
|
22250
|
+
dialog?.close({ animate: false });
|
|
22068
22251
|
trigger.remove();
|
|
22069
22252
|
}
|
|
22070
22253
|
};
|
|
@@ -23049,14 +23232,54 @@ function mountReplayTransport(deps) {
|
|
|
23049
23232
|
};
|
|
23050
23233
|
const offStrings = i18n.onChange(sync);
|
|
23051
23234
|
sync();
|
|
23235
|
+
let destroyed = false;
|
|
23052
23236
|
return {
|
|
23053
23237
|
element: bar,
|
|
23238
|
+
enter() {
|
|
23239
|
+
const view = bar.ownerDocument.defaultView;
|
|
23240
|
+
const reduceMotion = typeof view?.matchMedia === "function" && view.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
23241
|
+
if (destroyed || !bar.isConnected) return;
|
|
23242
|
+
if (reduceMotion) {
|
|
23243
|
+
bar.dataset.state = "open";
|
|
23244
|
+
return;
|
|
23245
|
+
}
|
|
23246
|
+
bar.dataset.state = "opening";
|
|
23247
|
+
bar.getBoundingClientRect();
|
|
23248
|
+
bar.dataset.state = "open";
|
|
23249
|
+
},
|
|
23054
23250
|
sync,
|
|
23055
|
-
destroy() {
|
|
23251
|
+
destroy({ animate = false } = {}) {
|
|
23252
|
+
if (destroyed) return;
|
|
23253
|
+
destroyed = true;
|
|
23056
23254
|
releasePicking();
|
|
23057
23255
|
for (const handle2 of [...open2]) handle2.close();
|
|
23058
23256
|
offStrings();
|
|
23059
|
-
bar.
|
|
23257
|
+
const view = bar.ownerDocument.defaultView;
|
|
23258
|
+
const reduceMotion = typeof view?.matchMedia === "function" && view.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
23259
|
+
if (!animate || reduceMotion || !bar.isConnected || !view) {
|
|
23260
|
+
bar.remove();
|
|
23261
|
+
return;
|
|
23262
|
+
}
|
|
23263
|
+
bar.style.pointerEvents = "none";
|
|
23264
|
+
bar.inert = true;
|
|
23265
|
+
bar.removeAttribute("role");
|
|
23266
|
+
bar.setAttribute("aria-hidden", "true");
|
|
23267
|
+
let timer = null;
|
|
23268
|
+
const remove = () => {
|
|
23269
|
+
if (timer !== null) clearTimeout(timer);
|
|
23270
|
+
bar.remove();
|
|
23271
|
+
};
|
|
23272
|
+
const onTransitionEnd = (event) => {
|
|
23273
|
+
if (event.target === bar) remove();
|
|
23274
|
+
};
|
|
23275
|
+
bar.dataset.state = "closing";
|
|
23276
|
+
const transitionDuration = view.getComputedStyle(bar).transitionDuration;
|
|
23277
|
+
if (!transitionDuration.split(",").some((value) => Number.parseFloat(value) > 0)) {
|
|
23278
|
+
remove();
|
|
23279
|
+
return;
|
|
23280
|
+
}
|
|
23281
|
+
bar.addEventListener("transitionend", onTransitionEnd, { once: true });
|
|
23282
|
+
timer = setTimeout(remove, 240);
|
|
23060
23283
|
}
|
|
23061
23284
|
};
|
|
23062
23285
|
}
|
|
@@ -23080,12 +23303,12 @@ function mountChrome(deps) {
|
|
|
23080
23303
|
};
|
|
23081
23304
|
prefetchSearch();
|
|
23082
23305
|
disposers.push(() => {
|
|
23083
|
-
searchDialog?.close();
|
|
23306
|
+
searchDialog?.close({ animate: false });
|
|
23084
23307
|
searchOwner.dispose();
|
|
23085
23308
|
deps.doors.openSearch = () => void 0;
|
|
23086
23309
|
});
|
|
23087
23310
|
disposers.push(i18n.onChange(() => {
|
|
23088
|
-
searchDialog?.close();
|
|
23311
|
+
searchDialog?.close({ animate: false });
|
|
23089
23312
|
searchOwner.dispose();
|
|
23090
23313
|
searchOwner = createSearchSessionOwner(deps.datafeed, { pageSize: 50 });
|
|
23091
23314
|
prefetchSearch();
|
|
@@ -23101,8 +23324,8 @@ function mountChrome(deps) {
|
|
|
23101
23324
|
let replayOwner = null;
|
|
23102
23325
|
let replayBar = null;
|
|
23103
23326
|
const replayWasUp = /* @__PURE__ */ new WeakMap();
|
|
23104
|
-
const releaseReplayRow = () => {
|
|
23105
|
-
replayBar?.destroy();
|
|
23327
|
+
const releaseReplayRow = (animate = true) => {
|
|
23328
|
+
replayBar?.destroy({ animate });
|
|
23106
23329
|
replayBar = null;
|
|
23107
23330
|
replayOwner = null;
|
|
23108
23331
|
};
|
|
@@ -23119,18 +23342,19 @@ function mountChrome(deps) {
|
|
|
23119
23342
|
replayOwner = target;
|
|
23120
23343
|
replayBar = mountReplayTransport({ chrome: overlays, i18n, icons: deps.icons, commands: target.commands, handle: target.chart, bars: target.bars, intraday: target.intraday });
|
|
23121
23344
|
deps.panes.after(replayBar.element);
|
|
23345
|
+
replayBar.enter();
|
|
23122
23346
|
}
|
|
23123
23347
|
replayBar?.sync();
|
|
23124
23348
|
};
|
|
23125
23349
|
disposers.push(() => {
|
|
23126
23350
|
deps.doors.replayChanged = () => void 0;
|
|
23127
|
-
releaseReplayRow();
|
|
23351
|
+
releaseReplayRow(false);
|
|
23128
23352
|
});
|
|
23129
23353
|
deps.doors.notify = notify;
|
|
23130
23354
|
let indicatorDialog = null;
|
|
23131
|
-
deps.doors.showIndicatorPicker = () => {
|
|
23355
|
+
deps.doors.showIndicatorPicker = (initialCollection) => {
|
|
23132
23356
|
if (!ui.indicatorPicker || indicatorDialog?.open()) return;
|
|
23133
|
-
indicatorDialog = openIndicatorPicker({ ...ctx, access: deps.access, storage: deps.storage, indicatorPicker: deps.indicatorPicker });
|
|
23357
|
+
indicatorDialog = openIndicatorPicker({ ...ctx, access: deps.access, storage: deps.storage, indicatorPicker: deps.indicatorPicker, initialCollection });
|
|
23134
23358
|
};
|
|
23135
23359
|
disposers.push(() => {
|
|
23136
23360
|
indicatorDialog?.close();
|
|
@@ -23140,7 +23364,7 @@ function mountChrome(deps) {
|
|
|
23140
23364
|
if (disposed) return;
|
|
23141
23365
|
if (request.mode !== "compare" && !ui.symbolSearch && !request.onPick) return;
|
|
23142
23366
|
if (request.mode === "compare" && !features.compare) return;
|
|
23143
|
-
searchDialog?.close();
|
|
23367
|
+
searchDialog?.close({ animate: false });
|
|
23144
23368
|
searchDialog = openSearchDialog({
|
|
23145
23369
|
host: overlays,
|
|
23146
23370
|
i18n,
|
|
@@ -23743,6 +23967,9 @@ function createChart(options) {
|
|
|
23743
23967
|
// Read on demand, because the chrome mounts below this object: a host asks for the slot when it
|
|
23744
23968
|
// has a door to put there, which is always after the widget exists.
|
|
23745
23969
|
chrome: {
|
|
23970
|
+
legendRows: (chartId, rows2) => {
|
|
23971
|
+
if (!disposed) instances.get(chartId)?.setLegendRows(rows2);
|
|
23972
|
+
},
|
|
23746
23973
|
topBar: (slot) => chrome2.topBarSlot(slot),
|
|
23747
23974
|
toolbarButton: (options2) => chrome2.toolbarButton(options2),
|
|
23748
23975
|
iconDiagnostics: () => iconDiagnostics.list()
|
|
@@ -23862,7 +24089,6 @@ function openSymbolSearch(options) {
|
|
|
23862
24089
|
return {
|
|
23863
24090
|
close() {
|
|
23864
24091
|
dialog.close();
|
|
23865
|
-
teardown();
|
|
23866
24092
|
}
|
|
23867
24093
|
};
|
|
23868
24094
|
}
|