-
Notifications
You must be signed in to change notification settings - Fork 76
Add initial victory-legend #116
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
import React from "react"; | ||
import { VictoryLegend } from "../../src/index"; | ||
|
||
export default class App extends React.Component { | ||
render() { | ||
const svgStyle = { border: "1px solid #ccc", padding: 20 }; | ||
const legendSize = { | ||
width: 500, | ||
height: 300 | ||
}; | ||
const data = [{ | ||
name: "Series 1", | ||
label: { | ||
fontSize: 10 | ||
}, | ||
symbol: { | ||
type: "circle" | ||
} | ||
}, { | ||
name: "Long Series Name", | ||
label: { | ||
fontSize: 12 | ||
}, | ||
symbol: { | ||
type: "triangleUp", | ||
style: { | ||
fill: "blue" | ||
} | ||
} | ||
}, { | ||
name: "Series 3", | ||
label: { | ||
fontSize: 14 | ||
}, | ||
symbol: { | ||
type: "diamond", | ||
style: { | ||
fill: "pink" | ||
} | ||
} | ||
}, { | ||
name: "Series 4", | ||
label: { | ||
fontSize: 16 | ||
}, | ||
symbol: { | ||
type: "plus" | ||
} | ||
}, { | ||
name: "Series 5", | ||
label: { | ||
fontSize: 18 | ||
}, | ||
symbol: { | ||
type: "star", | ||
style: { | ||
fill: "red" | ||
} | ||
} | ||
}]; | ||
|
||
return ( | ||
<div className="demo"> | ||
<VictoryLegend {...legendSize} data={data} style={svgStyle} /> | ||
<svg {...legendSize} style={svgStyle}> | ||
<VictoryLegend {...legendSize} orientation="horizontal" data={data} standalone={false}/> | ||
</svg> | ||
</div> | ||
); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,186 @@ | ||
/*global document:false */ | ||
|
||
import React, { PropTypes } from "react"; | ||
import { merge, assign, isEmpty } from "lodash"; | ||
import VictoryLabel from "../victory-label/victory-label"; | ||
import VictoryContainer from "../victory-container/victory-container"; | ||
import Point from "../point/point"; // it should be replaced | ||
|
||
const defaultFontStyle = { | ||
fontSize: 14, | ||
fontFamily: "'Gill Sans', 'Gill Sans MT', 'Seravek', 'Trebuchet MS', sans-serif", | ||
color: "#252525", | ||
backgroundColor: "#d9d9d9", | ||
stroke: "transparent" | ||
}; | ||
|
||
const defaultLegendStyle = { | ||
height: 200, | ||
width: 300, | ||
padding: 20 | ||
}; | ||
|
||
const defaultLegendData = [{ | ||
name: "Series 1", | ||
symbol: { | ||
type: "circle", | ||
style: { | ||
fill: "red" | ||
} | ||
} | ||
}, { | ||
name: "Series 2", | ||
symbol: { | ||
type: "diamond", | ||
style: { | ||
fill: "blue" | ||
} | ||
} | ||
}]; | ||
|
||
export default class VictoryLegend extends React.Component { | ||
static propTypes = { | ||
x: PropTypes.number.isRequired, | ||
y: PropTypes.number.isRequired, | ||
height: PropTypes.number.isRequired, | ||
width: PropTypes.number.isRequired, | ||
orientation: PropTypes.string, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe add an overall There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Still missing a |
||
data: PropTypes.arrayOf( | ||
PropTypes.shape({ | ||
name: PropTypes.string.isRequired, | ||
label: PropTypes.object, | ||
symbol: PropTypes.object | ||
}) | ||
), | ||
columnItemSpacing: PropTypes.number, | ||
rowItemSpacing: PropTypes.number, | ||
groupComponent: PropTypes.element, | ||
standalone: PropTypes.bool, | ||
style: PropTypes.object | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should the style object have namespaces so you can style the text, symbol, and container separately? I would suggest There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Legends should be able to work with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Legends should work with Victory's event system. This can be v2 work if you prefer, and I'm happy to implement it if you're having trouble with it. If you want to give it a shot, I've written about the pattern I'm using to implement events here https://github.com/FormidableLabs/builder-victory-component/blob/master/dev/CONTRIBUTING.md#events |
||
}; | ||
|
||
static defaultProps = { | ||
x: 0, | ||
y: 0, | ||
height: 100, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
width: 150, | ||
orientation: "vertical", | ||
columnItemSpacing: 10, | ||
rowItemSpacing: 8, | ||
symbolComponent: <Point/>, | ||
labelComponent: <VictoryLabel/>, | ||
containerComponent: <VictoryContainer/>, | ||
groupComponent: <g/>, | ||
standalone: true | ||
}; | ||
|
||
getFontStyles(props) { | ||
return merge({}, defaultFontStyle, props.label); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. if these are flat objects, prefer |
||
} | ||
|
||
getLegendStyle(props) { | ||
return { | ||
style: merge({}, defaultLegendStyle, props.style) | ||
}; | ||
} | ||
|
||
// it should be changed to approximateTextSize | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, please do use |
||
getLabelBbox(text, font) { | ||
const body = document.body; | ||
const container = document.createElement("svg"); | ||
const label = document.createElement("text"); | ||
|
||
label.innerText = text; | ||
label.setAttribute("style", | ||
`fill: ${font.color}; | ||
font-size: ${font.fontSize}px; | ||
font-family: ${font.fontFamily}; | ||
background-color: ${font.backgroundColor}; | ||
stroke: ${font.stroke}; | ||
top: -10000px; | ||
position: absolute;` | ||
); | ||
body.appendChild(container); | ||
container.appendChild(label); | ||
|
||
const bBox = label.getBoundingClientRect(); | ||
body.removeChild(container); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have concerns about this pattern. Everything will need to be extendable to the React Native version of Victory |
||
|
||
return bBox; | ||
} | ||
|
||
renderContainer(props, group) { | ||
return React.cloneElement( | ||
props.containerComponent, | ||
this.getLegendStyle(props), | ||
group | ||
); | ||
} | ||
|
||
renderGroup(children) { | ||
return React.cloneElement( | ||
this.props.groupComponent, | ||
{ role: "presentation" }, | ||
children | ||
); | ||
} | ||
|
||
render() { | ||
let yPadding = 0; | ||
let xPadding = 0; | ||
const props = this.props; | ||
const itemXPosition = []; | ||
const symbolComponents = []; | ||
const labelComponents = []; | ||
const { symbolComponent, labelComponent } = props; | ||
const data = isEmpty(props.data) ? defaultLegendData : props.data; | ||
|
||
data.forEach((series, index) => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For perf reasons we've been replacing array method that loop across data with length cached for loops |
||
const font = this.getFontStyles(series); | ||
const symbolSize = font.fontSize; | ||
const hPadding = symbolSize * 0.87; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Magic numbers... |
||
const yPos = props.y + index * yPadding; | ||
const labelSymbolPadding = symbolSize * 1.5 + props.columnItemSpacing; | ||
|
||
if (props.orientation === "horizontal") { | ||
itemXPosition.push(xPadding); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. assign by array index rather than |
||
xPadding += labelSymbolPadding + this.getLabelBbox(series.name, font).width; | ||
} else { | ||
yPadding = symbolSize + props.rowItemSpacing; | ||
} | ||
|
||
const xLabelPos = (props.orientation !== "horizontal" ? | ||
props.x : itemXPosition[index]) + hPadding; | ||
const xSymbolPos = props.orientation !== "horizontal" ? | ||
props.x : itemXPosition[index]; | ||
|
||
const symbolProps = assign({ | ||
key: `symbol-${index}` | ||
}, { | ||
x: xSymbolPos, | ||
y: yPos, | ||
size: series.symbol.size || symbolSize / 2.5, | ||
symbol: series.symbol.type | ||
}, series.symbol); | ||
|
||
const labelProps = assign({ | ||
key: `label-${index}` | ||
}, { | ||
x: xLabelPos, | ||
y: yPos, | ||
style: font, | ||
text: series.name | ||
}, series.label); | ||
|
||
symbolComponents[index] = React.cloneElement( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If you move all the stuff that is actually rendered into a new method it will be much easier to write the native version. I wrote up a little development guide with examples. You can see the patterns I've been using here https://github.com/FormidableLabs/builder-victory-component/blob/master/dev/CONTRIBUTING.md#flexibility There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could you please clarify it. |
||
symbolComponent, assign({}, symbolProps) | ||
); | ||
labelComponents[index] = React.cloneElement( | ||
labelComponent, assign({}, labelProps) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. unnecessary |
||
); | ||
}); | ||
|
||
const group = this.renderGroup([...symbolComponents, ...labelComponents]); | ||
return props.standalone ? this.renderContainer(props, group) : group; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you merge master you can use the point component
victory-primitives/point