Related
Question: How can I "reformat" the x-axis to be in ascending date, when the data contains a variable amount of dates within it?
I have some data that contains variable amounts of "date" associated with it. For example, Line One contains:
{ x: "2019-05-01", y: 2 },
{ x: "2019-06-01", y: 7 },
{ x: "2020-03-01", y: 1 }
Whereas Line Two contains:
{ x: "2019-05-01", y: 1 },
{ x: "2019-06-01", y: 5 },
{ x: "2020-05-01", y: 5 }
Code box: https://codesandbox.io/s/dreamy-almeida-x7rnd?fontsize=14&hidenavigation=1&theme=dark
The data is "uneven" on the x-axis, hence why it looks weird. I realise that if I were to put in "dummy dates" and data to it, then I can dictate the x-axis to become the format I want, the issue is dummy dates + data, because then it doesn't accurately model my database, so I want to avoid something like this.
What I've done so far is look over the documentation, I've tried adding in the type: "time" format to xscale, but I'm getting error
v.getTime() is not a function.
You have to add format to both ResponsiveLine Component and xScale property in order to use a time scale
xFormat:
Optional formatter for x values.
The formatted value can then be used for labels & tooltips.
If you use a time scale, you must provide a time format as values are converted to Date objects.
Complete config: (Working demo)
import React from "react";
import { ResponsiveLine } from "#nivo/line";
export default function App() {
return (
<div style={{ height: 350 }} className="App">
<ResponsiveLine
data={[
{
id: "LineOne",
data: [
{ x: "2019-05-01", y: 2 },
{ x: "2019-06-01", y: 7 },
{ x: "2020-03-01", y: 1 }
]
},
{
id: "LineTwo",
data: [
{ x: "2019-05-01", y: 1 },
{ x: "2019-06-01", y: 5 },
{ x: "2020-05-01", y: 5 }
]
},
{
id: "LineThree",
data: [
{ x: "2020-02-01", y: 4 },
{ x: "2020-03-01", y: 6 },
{ x: "2020-04-01", y: 1 }
]
}
]}
margin={{ top: 50, right: 110, bottom: 50, left: 60 }}
xScale={{
type: "time",
format: "%Y-%m-%d"
}}
xFormat="time:%Y-%m-%d"
yScale={{
type: "linear",
min: "auto",
max: "auto",
stacked: false,
reverse: false
}}
axisTop={null}
axisRight={null}
axisLeft={{
orient: "left",
tickSize: 5,
tickPadding: 5,
tickRotation: 0,
legend: "count",
legendOffset: -40,
legendPosition: "middle"
}}
axisBottom={{
format: "%b %d",
//tickValues: "every 2 days",
// tickRotation: -90,
legend: "time scale",
legendOffset: -12
}}
colors={{ scheme: "nivo" }}
pointSize={10}
pointColor={{ theme: "background" }}
pointBorderWidth={2}
pointBorderColor={{ from: "serieColor" }}
pointLabel="y"
pointLabelYOffset={-12}
useMesh={true}
legends={[
{
anchor: "bottom-right",
direction: "column",
justify: false,
translateX: 100,
translateY: 0,
itemsSpacing: 0,
itemDirection: "left-to-right",
itemWidth: 80,
itemHeight: 20,
itemOpacity: 0.75,
symbolSize: 12,
symbolShape: "circle",
symbolBorderColor: "rgba(0, 0, 0, .5)",
effects: [
{
on: "hover",
style: {
itemBackground: "rgba(0, 0, 0, .03)",
itemOpacity: 1
}
}
]
}
]}
/>
)
</div>
);
}
modified the following things and tested with random (none sorted data)
xscale type to time with the format
add indexBy="date" property to responsiveline element
add format: "%Y-%m-%d" to your axis bottom,
you can find a working example here
https://codesandbox.io/s/laughing-mestorf-zeozb
export default function App()
{ return (
<div style={{ height: 350 }} className="App">
<ResponsiveLine
data={[
{
id: "LineOne",
data: [
{ x: "2019-05-01", y: 2 },
{ x: "2019-06-01", y: 7 },
{ x: "2020-03-01", y: 1 },
{ x: "2017-09-01", y: 6 }
]
},
{
id: "LineTwo",
data: [
{ x: "2019-05-01", y: 1 },
{ x: "2019-06-01", y: 5 },
{ x: "2020-05-01", y: 5 },
{ x: "2018-09-01", y: 3 }
]
},
{
id: "LineThree",
data: [
{ x: "2020-02-01", y: 4 },
{ x: "2020-03-01", y: 6 },
{ x: "2020-04-01", y: 1 }
]
}
]}
indexBy="date"
margin={{ top: 50, right: 110, bottom: 50, left: 60 }}
xScale={{
type: "time",
format: "%Y-%m-%d",
precision: "day"
}}
yScale={{ type: "linear", stacked: false, min: 0, max: "auto" }}
axisTop={null}
axisRight={null}
axisBottom={{
orient: "bottom",
tickSize: 5,
format: "%Y-%m-%d",
tickPadding: 5,
tickRotation: -65,
legend: "time",
legendOffset: 40,
legendPosition: "middle"
}}
axisLeft={{
orient: "left",
tickSize: 5,
tickPadding: 5,
tickRotation: 0,
legend: "count",
legendOffset: -40,
legendPosition: "middle"
}}
colors={{ scheme: "nivo" }}
pointSize={10}
pointColor={{ theme: "background" }}
pointBorderWidth={2}
pointBorderColor={{ from: "serieColor" }}
pointLabel="y"
pointLabelYOffset={-12}
useMesh={true}
legends={[
{
anchor: "bottom-right",
direction: "column",
justify: false,
translateX: 100,
translateY: 0,
itemsSpacing: 0,
itemDirection: "left-to-right",
itemWidth: 80,
itemHeight: 20,
itemOpacity: 0.75,
symbolSize: 12,
symbolShape: "circle",
symbolBorderColor: "rgba(0, 0, 0, .5)",
effects: [
{
on: "hover",
style: {
itemBackground: "rgba(0, 0, 0, .03)",
itemOpacity: 1
}
}
]
}
]}
/>
)
</div>
);
}
Another thing to note is that in the above answers, the date on the actual chart is -1 from the date in the data. To solve this, add "useUTC: false" to your xScale props.
I have figured out how to set the size for an annotation individually:
labels: [
{
point: {
x: chart.xAxis[0].max - 0.1,
y: 50,
xAxis: 0,
yAxis: 0
},
height: 100,
shape: "rect",
text: "test",
verticalAlign: "bottom"
}
]
I would like it to position between 50 and 150. How can I achieve that?
And additionally, I would like the text to be aligned in the center of the box?!
You can calculate the height by using toPixels method. You have to also take a padding into account. To center the text you can use asynchronous function, because annotations do not exist in load event yet.
chart: {
events: {
load: function() {
const chart = this;
const height =
chart.yAxis[0].toPixels(50) - chart.yAxis[0].toPixels(150);
chart.addAnnotation({
labels: [
{
point: {
x: chart.xAxis[0].max - 0.1,
y: 150,
xAxis: 0,
yAxis: 0
},
y: 0,
padding: 0,
height: height,
shape: "rect",
text: "test",
verticalAlign: "top"
}
]
});
}
}
}
Live demo: https://codesandbox.io/s/qqqkx2zr49
API: https://api.highcharts.com/class-reference/Highcharts.Axis#toPixels
I'm using multichart with line and scatterplot. The line chart works fine but the scatterplot data does not plot correctly for the x-axis data.
Can someone please provide a working example other than the one on github? Here is the selections:
$scope.options = {
chart: {
type: 'multiChart',
height: 450,
margin: {
top: 30,
right: 60,
bottom: 50,
left: 70
},
color: d3.scale.category10().range(),
//useInteractiveGuideline: true,
duration: 500,
xAxis: {
ticks: 10,
tickFormat: function (d) {
return d3.format(',10d')(d);
}
},
yAxis1: {
ticks: 10,
tickFormat: function (d) {
return d3.format('10d')(d);
}
}
}
};
function generateData() {
var data1 = [{ x: 0, y: 25 }, { x: 25, y: 25 }, { x: 25, y: 0 }];
var data2 = [{ x: 0, y: 50 }, { x: 50, y: 50 }, { x: 50, y: 0 }];
var data3 = [{ x: 0, y: 75 }, { x: 75, y: 75 }, { x: 75, y: 0 }];
var data4 = [{ x: 0, y: 100 }, { x: 100, y: 100 }, { x: 100, y: 0 }];
var scatter = [{ x: 10, y: 30, size: Math.random(), shape: 'circle' }, { x: 20, y: 50, size: Math.random(), shape: 'circle' }, { x: 30, y: 80, size: Math.random(), shape: 'circle' }];
var testdata = [];
testdata.push({ key: 'Stream1', values: data1 });
testdata.push({ key: 'Stream2', values: data2 });
testdata.push({ key: 'Stream3', values: data3 });
testdata.push({ key: 'Stream4', values: data4 });
testdata.push({ key: 'Stream5', values: scatter });
testdata[0].type = 'line';
testdata[0].yAxis = 1;
testdata[1].type = 'line';
testdata[1].yAxis = 1;
testdata[2].type = 'line';
testdata[2].yAxis = 1;
testdata[3].type = 'line';
testdata[3].yAxis = 1;
testdata[4].type = 'scatter';
testdata[4].yAxis = 1;
return testdata;
}
I also encounter this problem, and my workaround is to add a dummy scatter series with the start and end point only, and the series color is transparent.
For example:
line series: 1,2,3,4,5...100
scatter 1: 5, 67, 83
dummy scatter: 1, 100
Then the x-domain of the scatter will scale with the line series
I have extended a component and I can't get it to render to a panel. Seems like I am missing something pretty basic since the debugger in Chrome actually shows that the panel has the data, it just isn't showing up.
Here is the fiddle and here are parts of the code:
Custom Component:
Ext.define('StoplightControl', {
extend: 'Ext.draw.Component',
alias: 'widget.Stoplight',
constructor: function (config) {
this.initConfig(config);
this.callParent(arguments);
},
initComponent: function () {
var kpiData; // = Ext.data.StoreManager.get('KPIStore').getById(this.model.get('KPI_ID'));
if (typeof (kpiData) === 'undefined' || kpiData == null) {
kpiData = Ext.create('KPIModel', {
ControlBackground: '#000000',
Caution_label: 'On',
Good_label: 'Ahead',
Poor_label: 'Behind',
Good_color: '#00FF00',
Poor_color: '#ff0000',
Caution_color: '#550000',
Header: 'Test'
});
}
this.setGradients(kpiData.get('ControlBackground'));
this.drawItems(this.model, kpiData);
},
setGradients: function (controlColor) {
this.gradients = [{
id: 'middleGradient',
angle: 180,
stops: {
0: {
color: controlColor,
opacity: 1
},
50: {
color: controlColor,
opacity: .6
},
100: {
color: controlColor,
opacity: 1
}
}
}, {
id: 'lightGradient1',
angle: -90,
stops: {
0: {
color: '#ffffff',
opacity: 0.01
},
100: {
color: '#ffffff',
opacity: .8
}
}
}]
},
drawItems: function (model, kpiData) {
var cautionValueX = -22.5 * (model.get('cautionValue').toString().length) + 227.5,
goodValueX = -22.5 * (model.get('goodValue').toString().length) + 227.5,
poorValueX = -22.5 * (model.get('poorValue').toString().length) + 227.5,
maxLineLength = 15,
changeOfY = -50,
cautionLabel = linebreaks(kpiData.get('Caution_label'), maxLineLength),
goodLabel = linebreaks(kpiData.get('Good_label'), maxLineLength),
poorLabel = linebreaks(kpiData.get('Poor_label'), maxLineLength),
cautionChangeY = (cautionLabel.split("\n").length - 1) * changeOfY,
goodChangeY = (goodLabel.split("\n").length - 1) * changeOfY,
poorChangeY = (poorLabel.split("\n").length - 1) * changeOfY,
headerFontSize = '100px arial,sans-serif',
textFontSize = '80px arial,sans-serif';
var drawItems = [{
type: 'rect',
x: 1.6620979,
y: 52.362183,
radius: 90,
width: 448.10959,
height: 1000,
fill: 'url(#middleGradient)',
stroke: 'none'
}, {
type: "circle",
radius: 140,
x: 224,
y: 896,
stroke: "#000000",
'stroke-width': 1,
fill: kpiData.get('Good_color')
}, {
type: "circle",
x: 224,
y: 214,
radius: 140,
stroke: "#000000",
'stroke-width': 1,
fill: kpiData.get('Poor_color')
}, {
type: "circle",
x: 224,
y: 555,
radius: 140,
stroke: "#000000",
'stroke-width': 1,
fill: kpiData.get('Caution_color')
}, {
type: "text",
id: "svg-stoplight-poorValue",
text: model.get('poorValue'),
x: poorValueX,
y: 220,
fill: "Black",
font: textFontSize
}, {
type: "text",
id: "svg-stoplight-cautionValue",
text: model.get('cautionValue'),
x: cautionValueX,
y: 560,
fill: "Black",
font: textFontSize
}, {
type: "text",
id: "svg-stoplight-goodValue",
text: model.get('goodValue'),
x: goodValueX,
y: 900,
fill: "Black",
font: textFontSize
}, {
type: "text",
id: "svg-stoplight-poorLabel",
text: poorLabel,
x: 500,
y: 220 + poorChangeY,
fill: "Black",
font: textFontSize
}, {
type: "text",
id: "svg-stoplight-cautionLabel",
text: cautionLabel,
x: 500,
y: 560 + cautionChangeY,
fill: "Black",
font: textFontSize
}, {
type: "text",
id: "svg-stoplight-goodLabel",
text: goodLabel,
x: 500,
y: 900 + goodChangeY,
fill: "Black",
font: textFontSize
}, {
type: "text",
id: "svg-stoplight-headerLabel",
text: kpiData.get('Header'),
x: 145,
y: -40,
fill: "Black",
font: headerFontSize
}, {
type: "text",
id: "svg-stoplight-totalLabel",
text: "Total = " + model.get('total'),
x: 100,
y: 1250,
fill: "Black",
font: textFontSize
}];
//don't add gradients if IE is > 10 or documentMode is less than 9
if (!(ie > 10 || document.documentMode < 9)) {
drawItems.push({
type: "ellipse",
id: 'test1',
radiusX: 112,
radiusY: 80,
x: 224,
y: 156,
fill: 'url(#lightGradient1)'
}, {
type: "ellipse",
radiusX: 112,
radiusY: 80,
x: 224,
y: 498,
fill: 'url(#lightGradient1)'
}, {
type: "ellipse",
radiusX: 112,
radiusY: 80,
x: 224,
y: 838,
fill: 'url(#lightGradient1)'
});
}
},
width: 210,
height: 250
});
Creation of the Panel and adding the component:
var displayPanel = Ext.create('widget.panel', {
width: 600,
height: 800,
title: 'Cost & Schedule Variance',
renderTo: 'WorkstreamStoplights',
pack: 'center',
shrinkWrap: 3,
layout: {
type: 'table',
column: 2
},
});
stoplightStore.each(function (model, idx) {
var stoplight = Ext.create('StoplightControl', {
model: model
});
displayPanel.add(stoplight);
});
displayPanel.doLayout();
As you'll be able to see from the fiddle, the Title displays properly and I have even added an item to the displayPanel on creation, but doing a .add() doesn't seem to have any effect even with the .doLayout()
Haven't dabbled in ExtJS for a long time, but the 4.2 doc states, for this method:
This method needs to be called whenever you change something on this
component that requires the Component's layout to be recalculated.
Ah, silly me. I needed to add items to my component. I needed to make the assignment in the initComponents of
this.items = drawItems;
Working fiddle
I can't be the only person with this problem, but my searches have left me lost and confused.
I have a custom Ext.draw.Component that uses a gradient fill. My understanding is that older browsers don't support gradients, but I thought Sencha accounted for that with by adding CSS to the object rendered.
Component in Chrome:
Component in IE:
My questions are thus:
Can Ext.js render gradients to IE8 and IE9? (IE10 displays fine)
Is there a way to determine if the browser is going to not render the gradient and I can just display something else (i.e. the compenent in IE wouldn't look so bad without the white elipses)?
UPDATE
Here is the code that generates the Stoplights. http://jsfiddle.net/wilsjd/Ts2qU/10/
var sencha = Ext.create('Ext.draw.Component', {
width: 170,
height: 170,
renderTo: this.get('elementToRenderTo'),
gradients: [{
id: 'middleGradient',
angle: 180,
stops: {
0: {
color: this.get('backgroundColor'),
opacity: 1
},
50: {
color: this.get('backgroundColor'),
opacity: .6
},
100: {
color: this.get('backgroundColor'),
opacity: 1
}
}
}, {
id: 'lightGradient1',
angle: -90,
stops: {
0: {
color: '#ffffff',
opacity: 0.01
},
100: {
color: '#ffffff',
opacity: .8
}
}
}, {
type: 'radial',
id: 'radialGradient1',
centerX:123.88702,
centerY:391.47498,
radius:166.47372,
focalX:124.57159,
focalY:391.47498,
stops: {
0: {
color: '#ffffff',
opacity: 0.01
},
100: {
color: '#ffffff',
opacity: .8
}
}
}],
items: [{
type: 'rect',
x: 1.6620979,
y: 52.362183,
radius: 90,
width: 448.10959,
height: 1000,
fill: 'url(#middleGradient)',
stroke: 'none'
}, {
type: "circle",
radius: 140,
x:224,
y:896,
stroke: "#000000",
'stroke-width': 7,
fill: this.get('goodColor')
}, {
type: "circle",
x:224,
y:214,
radius:140,
stroke: "#000000",
'stroke-width': 7,
fill: this.get('poorColor')
}, {
type: "circle",
x:224,
y:555,
radius: 140,
stroke: "#000000",
'stroke-width': 7,
fill: this.get('cautionColor')
}, {
type: "ellipse",
radiusX: 112,
radiusY: 80,
x: 224,
y: 156,
fill: 'url(#lightGradient1)'
}, {
type: "ellipse",
radiusX: 112,
radiusY: 80,
x: 224,
y: 498,
fill: 'url(#lightGradient1)'
}, {
type: "ellipse",
radiusX: 112,
radiusY: 80,
x: 224,
y: 838,
fill: 'url(#lightGradient1)'
}, {
type: "text",
text: this.get('poorValue'),
x: poorValueX,
y: 210,
fill: "Black",
font: "80px bold"
}, {
type: "text",
text: this.get('cautionValue'),
x: cautionValueX,
y: 550,
fill: "Black",
font: "80px bold"
}, {
type: "text",
text: this.get('goodValue'),
x: goodValueX,
y: 890,
fill: "Black",
font: "80px bold"
}, {
type: "text",
text: poorLabel,
x: 500,
y: 210 + poorChangeY,
fill: "Black",
font: "80px bold"
}, {
type: "text",
text: cautionLabel,
x: 500,
y: 550 + cautionChangeY,
fill: "Black",
font: "80px bold"
}, {
type: "text",
text: goodLabel,
x: 500,
y: 890 + goodChangeY,
fill: "Black",
font: "80px bold"
}, {
type: "text",
text: this.get('title'),
x: 0,
y: 0,
fill: "Black",
font: "100px bold"
}, {
type: "text",
text: "Total = " + this.get('total'),
x: 100,
y: 1150,
fill: "Black",
font: "80px bold"
}]
});
Ext.js can indeed render gradients in IE, but in this case it isn't working (still need source to determine why)
Added if statement to determine if the browser was IE8 or IE9 (I checked the document mode too - IE10 doesn't work if in IE8 document mode), and then add my ellipses)