<html>
<head>
<title>Context Menu demo</title>
<!-- Fix for iOS Safari zooming bug -->
<meta
name="viewport"
content="width=device-width,initial-scale=1.0,maximum-scale=1.0,minimum-scale=1.0"
/>
<script
type="text/javascript"
src="https://charting-library.tradingview-widget.com/charting_library/charting_library.standalone.js"
></script>
<script
type="text/javascript"
src="https://charting-library.tradingview-widget.com/datafeeds/udf/dist/bundle.js"
></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/jquery-contextmenu/2.7.1/jquery.contextMenu.min.css"
/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-contextmenu/2.7.1/jquery.contextMenu.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-contextmenu/2.7.1/jquery.ui.position.js"></script>
</head>
<body style="margin: 0px">
<div id="tv_chart_container"></div>
<div class="context-menu"></div>
</body>
</html>
xxxxxxxxxx
div#symbol-text-source-ticker-and-description {
display: none;
}
xxxxxxxxxx
let lastMenuPos = null;
function initOnReady() {
var widget = (window.tvWidget = new TradingView.widget({
library_path:
"https://charting-library.tradingview-widget.com/charting_library/",
// debug: true, // uncomment this line to see Library errors and warnings in the console
fullscreen: true,
symbol: "AAPL",
interval: "1D",
container: "tv_chart_container",
datafeed: new Datafeeds.UDFCompatibleDatafeed(
"https://demo-feed-data.tradingview.com"
),
locale: "en",
context_menu: {
renderer_factory: function (items, params, onDestroy) {
console.log("Name of the menu | ", params);
console.log("Items composing menu | ", items);
onDestroyInternal = onDestroy;
return {
show: function (position) {
lastMenuPos = { x: position.clientX, y: position.clientY };
ctxItems = items.map(convertCtxItem).reduce(function (acc, item) {
acc[item.id] = item;
return acc;
}, {});
$(".context-menu").contextMenu(lastMenuPos);
},
hide: function () {
$(".context-menu").contextMenu("hide");
},
isShown: function () {
return Boolean(onDestroyInternal);
},
};
},
items_processor: function(items, actionsFactory, params) {
console.log(`Menu name is: ${params.menuName}`);
return Promise.resolve(items);
},
},
}));
}
window.addEventListener("DOMContentLoaded", initOnReady, false);
let ctxItems = {};
let onDestroyInternal = null;
$.contextMenu({
// This selector should be a selector for an element added above
selector: ".context-menu",
events: {
// Advanced Charts requires to call onDestroy function every time a context menu is destroyed/ hidden
// So create a mock for this event
hide: () => {
if (onDestroyInternal !== null) {
onDestroyInternal();
onDestroyInternal = null;
}
},
},
// To make this menu "dynamic", declare items via `build` method,
// so jQuery contextMenu will call this method every time when you would like to display a menu
build: () => {
return { items: ctxItems };
},
});
// Note that not all properties are used in this sample
// For the complete list of all available methods and properties please refer to typescript declaration file
function convertCtxItem(item) {
if (item.type === "separator") {
return {
id: item.id,
type: "cm_separator",
};
}
let state = item.getState();
// If an item is in loading state this means that there is no any data for it yet,
// so you need to display a spinner (or a loader) instead of this item.
// Alternatively, you could wait for all the items are loaded
// but it will delay a moment when a user sees a menu.
if (state.loading) {
// Display "Loading" text for such items
const result = {
id: item.id,
name: "Loading...",
};
// Wait until this item is updated and ready to be displayed
item.onUpdate().subscribe(
null,
(action) => {
// ToDo: jQuery contextMenu is limited and doesn't allow to update an item once it is displayed,
// But in your app make sure that you handle this correctly.
// An item might be async and its information will be provided later.
// In this callback, you'll receive a new information for this item.
// So you can use `action` argument to update the visual representation of the item.
// This trick recreates the whole menu which is not good from many points of view.
// But this is just a PoC so feel free to handle it differently in your code.
// Update the action state first
Object.assign(result, convertCtxItem(action));
// Hide and display a menu at the same position
$(".context-menu").contextMenu("hide");
$(".context-menu").contextMenu(lastMenuPos);
},
true
);
return result;
}
return {
id: item.id,
name: state.label + " (Test text)",
callback: () => item.execute(),
type: state.checkable ? "checkbox" : null,
selected: state.checked,
events: !state.checkable
? null
: {
change: () => {
item.execute();
},
},
// An item might have sub-items so you need to convert them as well
items:
state.subItems.length === 0
? null
: state.subItems.map(convertCtxItem).reduce((acc, item) => {
acc[item.id] = item;
return acc;
}, {}),
};
}
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.