Related
Two custom feature added in toolbox. I need functionality where in we can reset one feature on click of another.
like existing feature 'zoom' button and 'horizontally select' brush button. If we click the zoom button first and then click on brush button then zoom button automatically gets reset to default.
below is the reference code -
`function func(x) {
x /= 10;
return Math.random() * 22;
}
function generateData() {
let data = [];
for (let i = 0; i <= 10; i += 0.1) {
data.push([i, func(i)]);
}
return data;
}
var isPointSelect = false;
var rangeSelector = false;
option = {
animation: false,
toolbox: {
feature: {
dataZoom: {
yAxisIndex: false
},
myRangeSelector: {
show: true,
title: 'Range Selector',
icon: 'path://M432.45,595.444c0,2.177-4.661,6.82-11.305,6.82c-6.475,0-11.306-4.567-11.306-6.82s4.852-6.812,11.306-6.812C427.841,588.632,432.452,593.191,432.45,595.444L432.45,595.444z M421.155,589.876c-3.009,0-5.448,2.495-5.448,5.572s2.439,5.572,5.448,5.572c3.01,0,5.449-2.495,5.449-5.572C426.604,592.371,424.165,589.876,421.155,589.876L421.155,589.876z M421.146,591.891c-1.916,0-3.47,1.589-3.47,3.549c0,1.959,1.554,3.548,3.47,3.548s3.469-1.589,3.469-3.548C424.614,593.479,423.062,591.891,421.146,591.891L421.146,591.891zM421.146,591.891',
onclick: function (model, api, type) {
// debugger;
rangeSelector = !rangeSelector;
this.model.setIconStatus(type, rangeSelector ? 'emphasis' : 'normal');
}
},
myPointSelect: {
show: true,
title: 'Point Select',
icon: 'path://M432.45,595.444c0,2.177-4.689,6.82-11.305,6.82r-6.475,0-11.306-4.567-11.306-6.82s4.852-6.812,11.306-6.812C427.841,588.632,432.452,593.191,432.45,595.444L432.45,595.444z M421.155,589.876c-3.009,0-5.448,2.495-5.448,5.572s2.439,5.572,5.448,5.572c3.01,0,5.449-2.495,5.449-5.572C426.604,592.371,424.165,589.876,421.155,589.876L421.155,589.876z M621.146,591.294c-1.916,0-3.47,1.589-3.47,3.549c0,1.959,1.554,3.548,3.47,3.548s3.469-1.589,3.469-3.548C424.614,593.479,423.062,591.891,421.146,591.891L421.146,591.891zM421.146,591.891',
onclick: function (model, api, type) {
isPointSelect = !isPointSelect;
this.model.setIconStatus(type, isPointSelect ? 'emphasis' : 'normal');
}
},
brush: {
type: ['lineX', 'clear', 'keep']
}
}
},
brush: {
xAxisIndex: 'all',
brushLink: 'all',
brushStyle: {
color: 'white'
},
outOfBrush: {
colorAlpha: 0.1
}
},
graphic: {
elements: []
},
grid: {
top: 100,
left: 50,
right: 40,
bottom: 50
},
xAxis: {
minorTick: {
show: true
},
minorSplitLine: {
show: true
}
},
yAxis: {
minorTick: {
show: true
},
minorSplitLine: {
show: true
}
},
series: [
{
type: 'line',
showSymbol: false,
clip: true,
data: generateData(),
markArea: {
data: []
}
}
]
};`
Echart has similar functionality for zoom and brush feature.
I created a chart with two y-axis and one x-axis and struggle to create tooltips with intersect = false, so it will start the tooltip when hovering over the line and not only the point of the chart
However, the whole settings for the tooltip, described on
https://www.chartjs.org/docs/latest/configuration/tooltip.html
Does not seem to have any effect for me. My code see below
I am using this code inside a NodeRed template node
\<canvas id="myChart" width=912 height =370\>\</canvas\>
\<script\>
var textcolor = getComputedStyle(document.documentElement).getPropertyValue('--nr-dashboard-widgetTextColor');
var gridcolor = '#E6E6E6';
var linecolors = \['#333F50','#B55A11', '#90C050'\]
var backgroundColor = \['#999FA7','#DAAC88', '#C7DFA7'\]
var ctx = document.getElementById('myChart').getContext('2d');
var chart = new Chart(ctx, {
// The type of chart we want to create
type: 'line',
// The data for our dataset
data: {
labels: {{{payload.labels}}},
datasets: [
{
label: 'Folie Länge Ist',
backgroundColor: backgroundColor[0],
borderColor: linecolors[0],
data: {{{payload.eins}}},
yAxisID: 'left-y-axis',
},
{
label: 'Folie Länge Soll',
backgroundColor: backgroundColor[1],
borderColor: linecolors[1],
data: {{{payload.zwei}}},
yAxisID: 'left-y-axis',
},
{
label: 'Aufwickler Durchmesser',
backgroundColor: backgroundColor[2],
borderColor: linecolors[2],
data: {{{payload.drei}}},
yAxisID: 'right-y-axis',
}
]
},
// Configuration options
options: {
animation: false,
legend: {
display: true,
position: "top"
},
elements: {
point: {
pointStyle: 'circle',
radius: 0
},
line: {
tension: 0,
fill: false
}
},
plugins: {
tooltip: {
mode: 'index',
intersect: false
}
},
scales: {
yAxes: [
{
gridLines :{display:false},
id: 'left-y-axis',
type: 'linear',
position: 'left',
ticks: {
fontColor: linecolors[0],
callback: function(value, index, ticks) {
return new Intl.NumberFormat('de-DE').format(value);
}
}
},
{
gridLines :{zeroLineColor:gridcolor,color:gridcolor,lineWidth:1},
id: 'right-y-axis',
type: 'linear',
position: 'right',
ticks: {
fontColor:linecolors[2],
callback: function(value, index, ticks) {
return new Intl.NumberFormat('de-DE').format(value);
}
}
}
],
xAxes: [
{
ticks: {
autoSkip: true,
autoSkipPadding: 10,
maxRotation: 0,
minRotation: 0
}
}
]
}
}
});
\</script\>
Tried setting intersect = false, also enable = false just to test if these settings have an effect
Can someone check my chart options and suggest a way to make the time xaxis behave correctly? I've tried with timestamps, dates, timestamps / 1000 and nothing looks right
let sales = [
0,84,5,3,2,1,0,0,3,6
]
let listings = [
1,297,23,5,8,6,9,3,6,19
]
let ps = [
1663084060653,
1663089644329,
1663095228005,
1663100811680,
1663106395356,
1663111979032,
1663117562708,
1663123146384,
1663128730059,
1663134313735
]
let color = "red"
option = {
textStyle: {
color
},
legend: {
textStyle: {
color
},
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow'
}
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true
},
xAxis: [
{
type: 'time',
data: ps,
// axisLabel: {
// formatter: ts => new Date(ts).toTimeString().replace(/ .*/, '')
// }
}
],
yAxis: [
{
// type: 'value'
}
],
series: [
{
name: 'Sales',
type: 'bar',
stack: 'Ad',
emphasis: {
focus: 'series'
},
data: sales
},
{
name: "Listings",
type: 'bar',
stack: 'Ad',
emphasis: {
focus: 'series'
},
data: listings
}
]
}
Your series (listings & sales here) have to have a [date, value] format. Also, you'll have to remove data from xAxis as it will automatically follow the dates that are given in the series.
So, in your example :
//convert listings & sales to a list of [date, value]
listings = listings.map((value, index) => {
return [ps[index], value]
})
sales = sales.map((value, index) => {
return [ps[index], value]
})
xAxis: [
{
type: 'time',
//data: ps, <--- remove this line
}
],
I try to build an input field for quantities with plus and minus buttons. More like to understand how composite controls are working. I found this documentation.
I thought I could use the new control like this:
new ex.perimental.Input({
width: "14em",
editable: true,
input: new sap.m.Input({
width: "8em",
value: {
path: "model>Quantity",
type: new sap.ui.model.type.Integer({}, {
minimum:0
})
},
description: "{model>Unit}"
}),
})
The control code looks like this:
sap.ui.define([
"sap/ui/core/Control",
"sap/m/Button",
"sap/m/Input"
], function(Control, Button, Input) {
"use strict";
return Control.extend("ex.perimental.Input", {
metadata: {
properties: {
width: {
type: "string",
defaultValue: "14em",
},
editable: {
type: "boolean",
defaultValue: false,
},
},
aggregations: {
_increaseButton: {
type: "sap.m.Button",
multiple: false,
visibility: "hidden",
},
_decreaseButton: {
type: "sap.m.Button",
multiple: false,
visibility: "hidden",
},
input: { type: "sap.m.Input", multiple: false },
_hBox: { type: "sap.m.HBox", multiple: false, visibility: "hidden" },
},
events: {
increase: {},
decrease: {},
},
},
_onDecrease: function(oEvent) {
var oResourceBundle = this.getModel("i18n").getResourceBundle();
var oldValue = this.getAggregation("input").getValue();
var newValue = 0;
if (!isNaN(Number(oldValue))) {
newValue = Number(oldValue) - 1;
}
oInput.setValue(newValue);
this.fireEvent("decrease", {
oldValue: oldValue,
newValue: newValue,
});
},
_onIncrease: function(oEvent) {
var oResourceBundle = this.getModel("i18n").getResourceBundle();
var oldValue = this.getAggregation("input").getValue();
var newValue = 0;
if (!isNaN(Number(oldValue))) {
newValue = Number(oldValue) + 1;
}
oInput.setValue(newValue);
this.fireEvent("increase", {
oldValue: oldValue,
newValue: newValue,
});
},
init: function() {
this.setAggregation(
"_decreaseButton",
new Button({
icon: "sap-icon://sys-minus",
press: this._onDecrease.bind(this),
})
);
this.setAggregation(
"_increaseButton",
new Button({
icon: "sap-icon://sys-add",
press: this._onIncrease.bind(this),
})
);
this.setAggregation(
"_hBox",
new sap.m.HBox({
items: [
this.getAggregation("_decreaseButton"),
this.getAggregation("_increaseButton"),
],
})
);
},
setEditable: function(sValue) {
debugger;
// aggregations will be null now
// I assume because they are reused in the HBox control
// this.getAggregation("_increaseButton").setEditable(sValue);
// this.getAggregation("_decreaseButton").setEditable(sValue);
// this.getAggregation("input").setEditable(sValue);
},
setWidth: function(sValue) {
this.getAggregation("_hBox").setWidth(sValue);
},
setInput: function(oInput) {
this.setAggregation("input", oInput);
var oHBox = this.getAggregation("_hBox");
oHBox.insertItem(oInput, 1);
},
renderer: function(oRenderManager, oControl) {
oRenderManager.write("<div");
oRenderManager.writeControlData(oControl);
oRenderManager.addClass("myStyle");
oRenderManager.writeClasses();
oRenderManager.write(">");
oRenderManager.renderControl(oControl.getAggregation("_hBox"));
oRenderManager.write("</div>");
}
});
});
It will be rendered but the setEditable is not working.
The buttons (used inside the HBox control again) are not reachable via getAggregation. Also the input field (set from outside) can't be accessed.
Not sure how to do it right. Anyone an idea?
Edit2
This is the latest version but still not working.
I am asking me how to put the externally defined input control into the right place inside the inner Hbox control and be able to access this control in methods like setEditable?
sap.ui.define([
"sap/ui/core/Control",
"sap/m/Button",
"sap/m/Input"
], function(Control, Button, Input) {
"use strict";
return Control.extend("ex.perimental.Input", {
metadata: {
properties: {
width: {
type: "string",
defaultValue: "14em",
},
editable: {
type: "boolean",
defaultValue: false,
},
},
aggregations: {
_hBox: { type: "sap.m.HBox", multiple: false, visibility: "hidden" },
},
associations: {
input: { type: "sap.m.Input", multiple: false, singularName: "input" },
},
events: {
increase: {},
decrease: {},
},
},
_onDecrease: function(oEvent) {
var oResourceBundle = this.getModel("i18n").getResourceBundle();
var oldValue = this._input.getValue();
var newValue = 0;
if (!isNaN(Number(oldValue))) {
newValue = Number(oldValue) - 1;
}
this._input.setValue(newValue);
this.fireEvent("decrease", {
oldValue: oldValue,
newValue: newValue,
});
},
_onIncrease: function(oEvent) {
var oResourceBundle = this.getModel("i18n").getResourceBundle();
var oldValue = this._input.getValue();
var newValue = 0;
if (!isNaN(Number(oldValue))) {
newValue = Number(oldValue) + 1;
}
this._input.setValue(newValue);
this.fireEvent("increase", {
oldValue: oldValue,
newValue: newValue,
});
},
init: function() {
this._decreaseButton = new Button({
icon: "sap-icon://sys-minus",
press: this._onDecrease.bind(this),
});
this._increaseButton = new Button({
icon: "sap-icon://sys-add",
press: this._onIncrease.bind(this),
});
this.setAggregation(
"_hBox",
new sap.m.HBox({
items: [
this._decreaseButton,
this.getAssociation("input"),
this._increaseButton,
],
})
);
},
setEditable: function(sValue) {
var bEditable = false;
if (sValue === true) {
bEditable = true;
}
this._decreaseButton.setEnabled(bEditable);
this._increaseButton.setEnabled(bEditable);
// seems not always working
this._input.setEditable(bEditable);
},
setWidth: function(sValue) {
this.getAggregation("_hBox").setWidth(sValue);
},
setInput: function(oInput) {
this.setAssociation("input", oInput);
this._input = oInput;
var oHBox = this.getAggregation("_hBox");
oHBox.insertItem(oInput, 1);
},
renderer: function(oRenderManager, oControl) {
oRenderManager.write("<div");
oRenderManager.writeControlData(oControl);
oRenderManager.addClass("myStyle");
oRenderManager.writeClasses();
oRenderManager.write(">");
oRenderManager.renderControl(oControl.getAggregation("_hBox"));
oRenderManager.write("</div>");
}
});
});
I still have problems with the association handling (updated code) I guess handling the association should be done different? Sometimes the input field is still null.
a control can only be aggregated by one control at a time.
This is the difference between associations (control may be at multiple at the same time) and aggregations.
What you can do in your init is:
this._decreaseButton = new Button (...)
Basically you only need one aggregation for your HBox.
If your buttons are then aggregated by the HBox they will know the models of the parent and also be destroyed.
The only thing you need to check if your Root control is a Ancestor of the created controls (use myControl.getParent()).
best regards,
Tobias
I'm working on a donut chart type, with the Highcharts library.
As you can see in the image below, some of the inner data labels are overlapped.
I've been playing with the parameter "distance" but doesn't fix this.
Find attached the code below,
// Create the chart
$(container).highcharts({
chart: {
type: 'pie'
},
credits: {
enabled: false
},
exporting: {
buttons: {
contextButton: {
symbol: 'url(/icon-turn-down.png)'
}
}
},
title: {
text: _title,
margin: 50
},
plotOptions: {
pie: {
shadow: false,
center: ['50%', '50%']
}
},
tooltip: {
formatter: function() {
var s = this.point.name.split('.');
if (s.length == 1) {
return this.y > 1? '<b>'+this.point.name+':</b> '+Highcharts.numberFormat(this.point.y) : null;
}
return this.y > 1? s[0]+'<br /><b>'+$.trim(s[1])+':</b> '+Highcharts.numberFormat(this.point.y) : null;
}
},
series: [{
name: '',
data: innerData,
size: '80%',
dataLabels: {
formatter: function() {
return this.y > 0 ? this.point.name : null;
},
color: 'white',
distance: -50
}
}, {
name: '',
data: outerData,
size: '100%',
innerSize: '80%',
dataLabels: {
formatter: function() {
var s = this.point.name.split('.');
if (s.length == 1) {
return this.y > 1 ? '<b>'+ this.point.name+':</b> '+ Highcharts.numberFormat(this.point.y) : null ;
}
s = this.point.name.substring(this.point.name.indexOf(".")+2);
return this.y > 1 ? '<b>'+ s+':</b> '+ Highcharts.numberFormat(this.point.y): null;
},
style: {
fontSize: "10px",
fontColor: "#000000"
}
}
}]
});
Finally, I found a solution, which is playing with the "startangle" attribute.
series: [{
name: '',
data: innerData,
startAngle:110,
size: '80%',
dataLabels: {
formatter: function() {
return this.y > 0 ? this.point.name : null;
},
color: 'white',
distance: -45
}, {
...
Distance parameter cannot be applied for each point, only general, so only what comes to my mind is iteared on each datalabel and use translate() function, or use formatter, apply CSS class and dhten use top/left parameter for each element. But will be helpful if you recreate your example as fiddle.