@trdrs/quickcharts 1.0.1 → 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 +8 -0
- package/README.md +11 -2
- package/dist/feature-manifest.json +1 -1
- package/dist/index.d.ts +1971 -1944
- package/dist/index.js +371 -128
- package/dist/index.js.map +1 -1
- package/dist/quickcharts.css +433 -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;
|
|
@@ -20909,6 +20979,7 @@ function buildSearchSurface(deps, box, frame) {
|
|
|
20909
20979
|
markDisposers = [];
|
|
20910
20980
|
for (const drop of drops) drop();
|
|
20911
20981
|
};
|
|
20982
|
+
let scopeMarkDisposer = null;
|
|
20912
20983
|
const input = h("input", { type: "text", role: "combobox", class: "qc-search-input", "aria-label": t("search.placeholder"), placeholder: t("search.placeholder"), autocomplete: "off", "aria-autocomplete": "list", "aria-expanded": "true", "aria-controls": listId, spellcheck: "false", value: query });
|
|
20913
20984
|
const clear = button2({ label: t("search.clear"), icon: deps.icons.glyph(ICONS.clear, { size: 18 }), className: "qc-search-op", onClick: () => setQuery("") });
|
|
20914
20985
|
const rule = compare ? null : h("span", { class: "qc-search-rule", "aria-hidden": "true" });
|
|
@@ -20948,25 +21019,37 @@ function buildSearchSurface(deps, box, frame) {
|
|
|
20948
21019
|
const classes = compare ? null : deps.classes();
|
|
20949
21020
|
let strip = null;
|
|
20950
21021
|
const classChips = /* @__PURE__ */ new Map();
|
|
20951
|
-
|
|
21022
|
+
const searchScope = deps.scope?.() ?? null;
|
|
21023
|
+
if (classes && classes.length > 0 || searchScope) {
|
|
20952
21024
|
strip = h("div", { class: "qc-search-classes", role: "group", "aria-label": t("search.classFilter") });
|
|
20953
|
-
|
|
20954
|
-
|
|
20955
|
-
const
|
|
20956
|
-
|
|
20957
|
-
|
|
20958
|
-
|
|
20959
|
-
|
|
20960
|
-
|
|
20961
|
-
|
|
20962
|
-
|
|
20963
|
-
|
|
20964
|
-
|
|
20965
|
-
|
|
20966
|
-
|
|
20967
|
-
|
|
20968
|
-
|
|
20969
|
-
|
|
21025
|
+
if (classes && classes.length > 0) {
|
|
21026
|
+
const all = [{ id: "", label: t("search.allClasses") }, ...classes.map((id) => ({ id, label: deps.classNames?.[id] ?? id }))];
|
|
21027
|
+
for (const c of all) {
|
|
21028
|
+
const chip = button2({
|
|
21029
|
+
label: c.label,
|
|
21030
|
+
text: c.label,
|
|
21031
|
+
className: "qc-chip qc-search-class",
|
|
21032
|
+
pressed: c.id === cls,
|
|
21033
|
+
onClick: () => {
|
|
21034
|
+
cls = c.id;
|
|
21035
|
+
for (const [id, b] of classChips) b.setAttribute("aria-pressed", String(id === cls));
|
|
21036
|
+
active = -1;
|
|
21037
|
+
search2.search(serverQuery(), cls);
|
|
21038
|
+
render();
|
|
21039
|
+
}
|
|
21040
|
+
});
|
|
21041
|
+
classChips.set(c.id, chip);
|
|
21042
|
+
strip.appendChild(chip);
|
|
21043
|
+
}
|
|
21044
|
+
}
|
|
21045
|
+
if (searchScope) {
|
|
21046
|
+
const mark = h("span", { class: "qc-search-scope-mark", "aria-hidden": "true" });
|
|
21047
|
+
const drop = searchScope.mark?.({ host: mark, size: SOURCE_MARK_SIZE });
|
|
21048
|
+
if (typeof drop === "function") {
|
|
21049
|
+
mark.dataset.qcHost = "true";
|
|
21050
|
+
scopeMarkDisposer = drop;
|
|
21051
|
+
} else if (mark.childNodes.length === 0) mark.textContent = searchScope.label.charAt(0).toUpperCase();
|
|
21052
|
+
strip.appendChild(h("span", { class: "qc-search-scope", "aria-label": searchScope.label }, mark, h("span", {}, searchScope.label)));
|
|
20970
21053
|
}
|
|
20971
21054
|
}
|
|
20972
21055
|
const list = h("div", { class: "qc-search-list", role: "listbox", id: listId, "aria-label": t("search.results") });
|
|
@@ -21135,6 +21218,8 @@ function buildSearchSurface(deps, box, frame) {
|
|
|
21135
21218
|
setDisabled(clear, false);
|
|
21136
21219
|
return {
|
|
21137
21220
|
teardown() {
|
|
21221
|
+
scopeMarkDisposer?.();
|
|
21222
|
+
scopeMarkDisposer = null;
|
|
21138
21223
|
releaseMarks();
|
|
21139
21224
|
search2.dispose();
|
|
21140
21225
|
observer?.disconnect();
|
|
@@ -21150,6 +21235,8 @@ function openSearchDialog(deps) {
|
|
|
21150
21235
|
host: deps.host,
|
|
21151
21236
|
label: title,
|
|
21152
21237
|
className: `qc-search-dialog qc-search-dialog--${mode}`,
|
|
21238
|
+
scrimClassName: "qc-search-dialog-scrim",
|
|
21239
|
+
exitMs: 140,
|
|
21153
21240
|
// The compare family (compare, and a compare row's change-symbol) shares one role for a host
|
|
21154
21241
|
// test to find; a symbol search, with or without a chart behind it, is the other.
|
|
21155
21242
|
role: mode === "compare" || mode === "change-symbol" ? "compare-dialog" : "symbol-search",
|
|
@@ -21929,127 +22016,238 @@ var TOGGLE_ROWS = [
|
|
|
21929
22016
|
{ leaf: "grid", label: "settings.gridLines" },
|
|
21930
22017
|
{ leaf: "sessions", label: "settings.sessionShading" }
|
|
21931
22018
|
];
|
|
22019
|
+
var nextSettingsId = 0;
|
|
21932
22020
|
function mountSettingsMenu(deps) {
|
|
21933
22021
|
const t = () => deps.i18n.t;
|
|
21934
|
-
|
|
22022
|
+
const settingsId = ++nextSettingsId;
|
|
22023
|
+
let dialog = null;
|
|
22024
|
+
let content = null;
|
|
22025
|
+
let activePage = "appearance";
|
|
22026
|
+
let committed = false;
|
|
22027
|
+
let initial = null;
|
|
21935
22028
|
const controls = [];
|
|
21936
22029
|
const retireControls = () => {
|
|
21937
22030
|
for (const control of controls) control.destroy();
|
|
21938
22031
|
controls.length = 0;
|
|
21939
22032
|
};
|
|
21940
|
-
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
|
+
});
|
|
21941
22045
|
trigger.setAttribute("aria-haspopup", "dialog");
|
|
21942
22046
|
trigger.setAttribute("aria-expanded", "false");
|
|
21943
22047
|
const apply = (partial) => {
|
|
21944
22048
|
deps.commands.execute("chart.appearance.apply", { appearance: partial });
|
|
21945
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
|
+
};
|
|
21946
22180
|
const open2 = () => {
|
|
21947
|
-
|
|
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({
|
|
21948
22190
|
host: deps.overlays,
|
|
21949
|
-
anchor: trigger,
|
|
21950
22191
|
label: t()("settings.menu"),
|
|
21951
|
-
|
|
21952
|
-
|
|
21953
|
-
|
|
21954
|
-
|
|
21955
|
-
build(
|
|
21956
|
-
|
|
21957
|
-
const
|
|
21958
|
-
const
|
|
21959
|
-
|
|
21960
|
-
|
|
21961
|
-
|
|
21962
|
-
|
|
21963
|
-
|
|
21964
|
-
|
|
21965
|
-
|
|
21966
|
-
|
|
21967
|
-
|
|
21968
|
-
|
|
21969
|
-
|
|
21970
|
-
|
|
21971
|
-
|
|
21972
|
-
|
|
21973
|
-
|
|
21974
|
-
|
|
21975
|
-
|
|
21976
|
-
|
|
21977
|
-
|
|
21978
|
-
});
|
|
21979
|
-
controls.push(control);
|
|
21980
|
-
body.appendChild(fieldRow(label2, control.element, { className: "qc-settings-row" }));
|
|
21981
|
-
}
|
|
21982
|
-
body.appendChild(menuSeparator());
|
|
21983
|
-
body.appendChild(menuHeading(t()("settings.sectionDisplay")));
|
|
21984
|
-
for (const row2 of TOGGLE_ROWS) {
|
|
21985
|
-
body.appendChild(switchRow({ label: t()(row2.label), checked: appearance[row2.leaf], disabled: !canApply, onChange: (on) => apply({ [row2.leaf]: on }) }));
|
|
21986
|
-
}
|
|
21987
|
-
body.appendChild(menuSeparator());
|
|
21988
|
-
body.appendChild(menuHeading(t()("settings.sectionScale")));
|
|
21989
|
-
const scale = chart.scaleMode();
|
|
21990
|
-
for (const option of SCALE_MODE_OPTIONS) {
|
|
21991
|
-
const id = `chart.scale.${option.id}`;
|
|
21992
|
-
body.appendChild(
|
|
21993
|
-
menuItem({
|
|
21994
|
-
text: commandLabel(deps, id),
|
|
21995
|
-
role: "menuitemradio",
|
|
21996
|
-
checked: option.id === scale,
|
|
21997
|
-
disabled: option.id !== scale && !deps.commands.available(id),
|
|
21998
|
-
onSelect: () => {
|
|
21999
|
-
deps.commands.execute(id);
|
|
22000
|
-
handle.refresh();
|
|
22001
|
-
}
|
|
22002
|
-
})
|
|
22003
|
-
);
|
|
22004
|
-
}
|
|
22005
|
-
body.appendChild(menuSeparator());
|
|
22006
|
-
body.appendChild(
|
|
22007
|
-
menuItem({
|
|
22008
|
-
text: commandLabel(deps, "chart.appearance.reset"),
|
|
22009
|
-
disabled: !deps.commands.available("chart.appearance.reset"),
|
|
22010
|
-
onSelect: () => {
|
|
22011
|
-
deps.commands.execute("chart.appearance.reset");
|
|
22012
|
-
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();
|
|
22013
22219
|
}
|
|
22014
22220
|
})
|
|
22015
22221
|
);
|
|
22016
|
-
|
|
22017
|
-
|
|
22018
|
-
|
|
22019
|
-
|
|
22020
|
-
|
|
22021
|
-
|
|
22022
|
-
|
|
22023
|
-
|
|
22024
|
-
text: commandLabel(deps, id),
|
|
22025
|
-
role: "menuitemradio",
|
|
22026
|
-
checked: themeMode === mode,
|
|
22027
|
-
disabled: themeMode !== mode && !deps.commands.available(id),
|
|
22028
|
-
onSelect: () => {
|
|
22029
|
-
deps.commands.execute(id);
|
|
22030
|
-
handle.refresh();
|
|
22031
|
-
}
|
|
22032
|
-
})
|
|
22033
|
-
);
|
|
22034
|
-
}
|
|
22035
|
-
}
|
|
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();
|
|
22036
22230
|
},
|
|
22037
22231
|
onClose: () => {
|
|
22038
22232
|
retireControls();
|
|
22039
|
-
|
|
22233
|
+
content = null;
|
|
22234
|
+
dialog = null;
|
|
22235
|
+
initial = null;
|
|
22236
|
+
trigger.setAttribute("aria-expanded", "false");
|
|
22040
22237
|
}
|
|
22041
22238
|
});
|
|
22239
|
+
trigger.setAttribute("aria-expanded", "true");
|
|
22042
22240
|
};
|
|
22043
22241
|
const sync = () => {
|
|
22044
22242
|
name(trigger, t()("settings.menu"));
|
|
22045
|
-
|
|
22243
|
+
refresh();
|
|
22046
22244
|
};
|
|
22047
22245
|
sync();
|
|
22048
22246
|
return {
|
|
22049
22247
|
element: trigger,
|
|
22050
22248
|
sync,
|
|
22051
22249
|
destroy() {
|
|
22052
|
-
|
|
22250
|
+
dialog?.close({ animate: false });
|
|
22053
22251
|
trigger.remove();
|
|
22054
22252
|
}
|
|
22055
22253
|
};
|
|
@@ -23034,14 +23232,54 @@ function mountReplayTransport(deps) {
|
|
|
23034
23232
|
};
|
|
23035
23233
|
const offStrings = i18n.onChange(sync);
|
|
23036
23234
|
sync();
|
|
23235
|
+
let destroyed = false;
|
|
23037
23236
|
return {
|
|
23038
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
|
+
},
|
|
23039
23250
|
sync,
|
|
23040
|
-
destroy() {
|
|
23251
|
+
destroy({ animate = false } = {}) {
|
|
23252
|
+
if (destroyed) return;
|
|
23253
|
+
destroyed = true;
|
|
23041
23254
|
releasePicking();
|
|
23042
23255
|
for (const handle2 of [...open2]) handle2.close();
|
|
23043
23256
|
offStrings();
|
|
23044
|
-
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);
|
|
23045
23283
|
}
|
|
23046
23284
|
};
|
|
23047
23285
|
}
|
|
@@ -23065,12 +23303,12 @@ function mountChrome(deps) {
|
|
|
23065
23303
|
};
|
|
23066
23304
|
prefetchSearch();
|
|
23067
23305
|
disposers.push(() => {
|
|
23068
|
-
searchDialog?.close();
|
|
23306
|
+
searchDialog?.close({ animate: false });
|
|
23069
23307
|
searchOwner.dispose();
|
|
23070
23308
|
deps.doors.openSearch = () => void 0;
|
|
23071
23309
|
});
|
|
23072
23310
|
disposers.push(i18n.onChange(() => {
|
|
23073
|
-
searchDialog?.close();
|
|
23311
|
+
searchDialog?.close({ animate: false });
|
|
23074
23312
|
searchOwner.dispose();
|
|
23075
23313
|
searchOwner = createSearchSessionOwner(deps.datafeed, { pageSize: 50 });
|
|
23076
23314
|
prefetchSearch();
|
|
@@ -23086,8 +23324,8 @@ function mountChrome(deps) {
|
|
|
23086
23324
|
let replayOwner = null;
|
|
23087
23325
|
let replayBar = null;
|
|
23088
23326
|
const replayWasUp = /* @__PURE__ */ new WeakMap();
|
|
23089
|
-
const releaseReplayRow = () => {
|
|
23090
|
-
replayBar?.destroy();
|
|
23327
|
+
const releaseReplayRow = (animate = true) => {
|
|
23328
|
+
replayBar?.destroy({ animate });
|
|
23091
23329
|
replayBar = null;
|
|
23092
23330
|
replayOwner = null;
|
|
23093
23331
|
};
|
|
@@ -23104,18 +23342,19 @@ function mountChrome(deps) {
|
|
|
23104
23342
|
replayOwner = target;
|
|
23105
23343
|
replayBar = mountReplayTransport({ chrome: overlays, i18n, icons: deps.icons, commands: target.commands, handle: target.chart, bars: target.bars, intraday: target.intraday });
|
|
23106
23344
|
deps.panes.after(replayBar.element);
|
|
23345
|
+
replayBar.enter();
|
|
23107
23346
|
}
|
|
23108
23347
|
replayBar?.sync();
|
|
23109
23348
|
};
|
|
23110
23349
|
disposers.push(() => {
|
|
23111
23350
|
deps.doors.replayChanged = () => void 0;
|
|
23112
|
-
releaseReplayRow();
|
|
23351
|
+
releaseReplayRow(false);
|
|
23113
23352
|
});
|
|
23114
23353
|
deps.doors.notify = notify;
|
|
23115
23354
|
let indicatorDialog = null;
|
|
23116
|
-
deps.doors.showIndicatorPicker = () => {
|
|
23355
|
+
deps.doors.showIndicatorPicker = (initialCollection) => {
|
|
23117
23356
|
if (!ui.indicatorPicker || indicatorDialog?.open()) return;
|
|
23118
|
-
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 });
|
|
23119
23358
|
};
|
|
23120
23359
|
disposers.push(() => {
|
|
23121
23360
|
indicatorDialog?.close();
|
|
@@ -23125,7 +23364,7 @@ function mountChrome(deps) {
|
|
|
23125
23364
|
if (disposed) return;
|
|
23126
23365
|
if (request.mode !== "compare" && !ui.symbolSearch && !request.onPick) return;
|
|
23127
23366
|
if (request.mode === "compare" && !features.compare) return;
|
|
23128
|
-
searchDialog?.close();
|
|
23367
|
+
searchDialog?.close({ animate: false });
|
|
23129
23368
|
searchDialog = openSearchDialog({
|
|
23130
23369
|
host: overlays,
|
|
23131
23370
|
i18n,
|
|
@@ -23138,6 +23377,7 @@ function mountChrome(deps) {
|
|
|
23138
23377
|
recents: widget.recents,
|
|
23139
23378
|
classes: () => deps.feedConfig()?.classes ?? null,
|
|
23140
23379
|
classNames: deps.classNames,
|
|
23380
|
+
scope: deps.scope,
|
|
23141
23381
|
painters: deps.painters,
|
|
23142
23382
|
curated: request.chart?.compare.symbols() ?? [],
|
|
23143
23383
|
request
|
|
@@ -23727,6 +23967,9 @@ function createChart(options) {
|
|
|
23727
23967
|
// Read on demand, because the chrome mounts below this object: a host asks for the slot when it
|
|
23728
23968
|
// has a door to put there, which is always after the widget exists.
|
|
23729
23969
|
chrome: {
|
|
23970
|
+
legendRows: (chartId, rows2) => {
|
|
23971
|
+
if (!disposed) instances.get(chartId)?.setLegendRows(rows2);
|
|
23972
|
+
},
|
|
23730
23973
|
topBar: (slot) => chrome2.topBarSlot(slot),
|
|
23731
23974
|
toolbarButton: (options2) => chrome2.toolbarButton(options2),
|
|
23732
23975
|
iconDiagnostics: () => iconDiagnostics.list()
|
|
@@ -23783,6 +24026,7 @@ function createChart(options) {
|
|
|
23783
24026
|
datafeed: options.datafeed,
|
|
23784
24027
|
feedConfig: () => feedConfig,
|
|
23785
24028
|
classNames: options.search?.classNames,
|
|
24029
|
+
scope: options.search?.scope,
|
|
23786
24030
|
painters,
|
|
23787
24031
|
indicatorPicker: options.indicatorPicker,
|
|
23788
24032
|
access: options.access,
|
|
@@ -23845,7 +24089,6 @@ function openSymbolSearch(options) {
|
|
|
23845
24089
|
return {
|
|
23846
24090
|
close() {
|
|
23847
24091
|
dialog.close();
|
|
23848
|
-
teardown();
|
|
23849
24092
|
}
|
|
23850
24093
|
};
|
|
23851
24094
|
}
|