Ability to save drawings

- Added `toolbox` to the common methods.
- `toolbox.save_drawings_under` can save drawings under a specific `topbar` widget. eg `chart.toolbox.save_drawings_under(chart.topbar[’symbol’]`)
- `toolbox.load_drawings` will load and display drawings stored under the tag/string given.
- `toolbox.export_drawings` will export all currently saved drawings to the given file path.
- `toolbox.import_drawings` will import the drawings stored at the given file path.

Fixes/Enhancements:
- `update` methods are no longer case sensitive.
- HorizontalLines no longer throw cyclic structure errors in the web console.
- `API` methods can now be normal methods or coroutines.
This commit is contained in:
louisnw
2023-07-20 21:52:17 +01:00
parent b2ceae59b7
commit 527130e618
10 changed files with 166 additions and 55 deletions

View File

@ -42,7 +42,7 @@ function makeSearchBox(chart) {
yPrice = param.point.y;
}
});
let selectedChart = true
let selectedChart = false
chart.wrapper.addEventListener('mouseover', (event) => {
selectedChart = true
})
@ -50,6 +50,7 @@ function makeSearchBox(chart) {
selectedChart = false
})
chart.commandFunctions.push((event) => {
if (!selectedChart) return
if (searchWindow.style.display === 'none') {
if (/^[a-zA-Z0-9]$/.test(event.key)) {
searchWindow.style.display = 'flex';

View File

@ -106,6 +106,12 @@ if (!window.HorizontalLine) {
this.chart.horizontal_lines.push(this)
}
toJSON() {
// Exclude the chart attribute from serialization
const {chart, line, ...serialized} = this;
return serialized;
}
updatePrice(price) {
this.chart.series.removePriceLine(this.line)
this.price = price
@ -361,36 +367,3 @@ function calculateTrendLine(startDate, startValue, endDate, endValue, interval,
}
return trendData;
}
/*
let customMenu = document.createElement('div')
customMenu.style.position = 'absolute'
customMenu.style.zIndex = '10000'
customMenu.style.background = 'rgba(25, 25, 25, 0.7)'
customMenu.style.color = 'lightgrey'
customMenu.style.display = 'none'
customMenu.style.borderRadius = '5px'
customMenu.style.padding = '5px 10px'
document.body.appendChild(customMenu)
function menuItem(text) {
let elem = document.createElement('div')
elem.innerText = text
customMenu.appendChild(elem)
}
menuItem('Delete drawings')
menuItem('Hide all indicators')
menuItem('Save Chart State')
let closeMenu = (event) => {if (!customMenu.contains(event.target)) customMenu.style.display = 'none';}
document.addEventListener('contextmenu', function (event) {
event.preventDefault(); // Prevent default right-click menu
customMenu.style.left = event.clientX + 'px';
customMenu.style.top = event.clientY + 'px';
customMenu.style.display = 'block';
document.removeEventListener('click', closeMenu)
document.addEventListener('click', closeMenu)
});
*/

View File

@ -66,7 +66,8 @@ if (!window.ToolBox) {
this.chart.chart.removeSeries(toDelete.line);
if (toDelete.ray) this.chart.chart.timeScale().setVisibleLogicalRange(logical)
}
this.drawings.splice(this.drawings.length - 1)
this.drawings.splice(this.drawings.indexOf(toDelete))
this.saveDrawings()
}
this.chart.commandFunctions.push((event) => {
if ((event.metaKey || event.ctrlKey) && event.code === 'KeyZ') {
@ -248,6 +249,7 @@ if (!window.ToolBox) {
this.chart.cursor = 'default'
this.chart.activeIcon.elem.style.backgroundColor = this.backgroundColor
this.chart.activeIcon = null
this.saveDrawings()
}
}
this.chart.chart.subscribeClick(this.clickHandler)
@ -263,6 +265,7 @@ if (!window.ToolBox) {
this.chart.cursor = 'default'
this.chart.activeIcon.elem.style.backgroundColor = this.backgroundColor
this.chart.activeIcon = null
this.saveDrawings()
}
onHorzSelect(toggle) {
!toggle ? this.chart.chart.unsubscribeClick(this.clickHandlerHorz) : this.chart.chart.subscribeClick(this.clickHandlerHorz)
@ -319,7 +322,6 @@ if (!window.ToolBox) {
let mouseDown = false
let clickedEnd = false
let checkForClick = (event) => {
//if (!hoveringOver) return
mouseDown = true
document.body.style.cursor = 'grabbing'
this.chart.chart.applyOptions({
@ -345,11 +347,11 @@ if (!window.ToolBox) {
this.chart.chart.subscribeCrosshairMove(checkForDrag)
}
originalIndex = this.chart.chart.timeScale().coordinateToLogical(x)
this.chart.chart.unsubscribeClick(checkForClick)
document.removeEventListener('mousedown', checkForClick)
}
let checkForRelease = (event) => {
mouseDown = false
document.body.style.cursor = 'pointer'
document.body.style.cursor = this.chart.cursor
this.chart.chart.applyOptions({handleScroll: true})
if (hoveringOver && 'price' in hoveringOver && hoveringOver.id !== 'toolBox') {
@ -359,6 +361,7 @@ if (!window.ToolBox) {
document.removeEventListener('mousedown', checkForClick)
document.removeEventListener('mouseup', checkForRelease)
this.chart.chart.subscribeCrosshairMove(hoverOver)
this.saveDrawings()
}
let checkForDrag = (param) => {
if (!param.point) return
@ -486,6 +489,54 @@ if (!window.ToolBox) {
})
this.drawings = []
}
saveDrawings() {
let drawingsString = JSON.stringify(this.drawings, (key, value) => {
if (key === '' && Array.isArray(value)) {
return value.filter(item => !(item && typeof item === 'object' && 'priceLine' in item && item.id !== 'toolBox'));
} else if (key === 'line' || (value && typeof value === 'object' && 'priceLine' in value && value.id !== 'toolBox')) {
return undefined;
}
return value;
});
this.chart.callbackFunction(`save_drawings__${this.chart.id}__${drawingsString}`)
}
loadDrawings(drawings) {
this.drawings = drawings
this.chart.chart.applyOptions({
handleScroll: false
})
let logical = this.chart.chart.timeScale().getVisibleLogicalRange()
this.drawings.forEach((item) => {
if ('price' in item) {
this.drawings[this.drawings.indexOf(item)] = new HorizontalLine(this.chart, 'toolBox', item.priceLine.price, item.priceLine.color, 2, item.priceLine.lineStyle, item.priceLine.axisLabelVisible)
}
else {
this.drawings[this.drawings.indexOf(item)].line = this.chart.chart.addLineSeries({
lineWidth: 2,
lastValueVisible: false,
priceLineVisible: false,
crosshairMarkerVisible: false,
autoscaleInfoProvider: () => ({
priceRange: {
minValue: 1_000_000_000,
maxValue: 0,
},
}),
})
let startDate = dateToChartTime(new Date(Math.round(chartTimeToDate(item.from).getTime() / this.interval) * this.interval), this.interval)
let endDate = dateToChartTime(new Date(Math.round(chartTimeToDate(item.to).getTime() / this.interval) * this.interval), this.interval)
let data = calculateTrendLine(startDate, item.data[0].value, endDate, item.data[item.data.length - 1].value, this.interval, this.chart, item.ray)
if (data.length !== 0) item.data = data
item.line.setData(data)
}
})
this.chart.chart.applyOptions({
handleScroll: true
})
this.chart.chart.timeScale().setVisibleLogicalRange(logical)
}
}
window.ToolBox = ToolBox