-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Copy the pygal.js file into the pyodide/packages/ directory
I think I have finished porting pygal.js from a Skulpt module to a plain JavaScript ES6 module which is now supported in Pyodide. More context: trinketapp/pygal.js#12
- Loading branch information
Showing
2 changed files
with
369 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,368 @@ | ||
export const config = { | ||
domOutput: () => { throw new Error("The config.domOutput function has not been set for pygal."); }, | ||
}; | ||
|
||
const COLORS = [ | ||
[255, 89, 149], [182, 227, 84], [254, 237, 108], [140, 237, 255], | ||
[158, 111, 254], [137, 156, 161], [248, 248, 242], [191, 70, 70], | ||
[81, 96, 131], [249, 38, 114], [130, 180, 20], [253, 151, 31], | ||
[86, 194, 214], [128, 131, 132], [140, 84, 254], [70, 84, 87] | ||
]; | ||
|
||
const some = (val) => typeof val !== "undefined"; | ||
const toJs = (val) => val?.toJs ? val.toJs() : val; | ||
|
||
class Chart { | ||
constructor({ title, width, height, range, include_x_axis, x_title, y_title, title_font_size, fill, stroke, x_labels } = {}) { | ||
const options = {}; | ||
if (some(title)) options.title = toJs(title); | ||
if (some(width)) options.width = toJs(width); | ||
if (some(height)) options.height = toJs(height); | ||
if (some(range)) options.range = { | ||
min: toJs(range)[0], | ||
max: toJs(range)[1], | ||
}; | ||
if (some(include_x_axis)) options.include_x_axis = toJs(include_x_axis); | ||
if (some(x_title)) options.x_title = toJs(x_title); | ||
if (some(y_title)) options.y_title = toJs(y_title); | ||
if (some(title_font_size)) options.title_font_size = toJs(title_font_size); | ||
if (some(fill)) options.fill = toJs(fill); | ||
if (some(stroke)) options.stroke = toJs(stroke); | ||
if (some(x_labels)) options.x_labels = toJs(x_labels); | ||
|
||
this._options = options; | ||
this._data = []; | ||
} | ||
|
||
add(label, values) { | ||
const data = [...toJs(values)].map(v => v || 0); | ||
|
||
this._data.unshift({ | ||
name: toJs(label), | ||
color: this.#rgba(COLORS[this._data.length % COLORS.length], 0.75), | ||
data: data, | ||
marker: { | ||
symbol: 'circle' | ||
}, | ||
stack: 1 | ||
}); | ||
|
||
return ''; | ||
} | ||
|
||
render() { | ||
const options = this._options; | ||
const elem = config.domOutput('<div></div>'); | ||
const title_style = { | ||
color: '#FFFFFF' | ||
}; | ||
if (options.title_font_size) { | ||
title_style['font-size'] = options.title_font_size + 'px'; | ||
} | ||
const xPlotLines = []; | ||
const yPlotLines = []; | ||
|
||
if (options.range) { | ||
yPlotLines.push({ | ||
value: options.range.min, | ||
width: 1, | ||
color: '#FFFFFF' | ||
}); | ||
} | ||
|
||
const defaultWidth = config.availableWidth || 400; | ||
const defaultHeight = Math.min(defaultWidth, config.availableHeight || 300); | ||
|
||
let chart = { | ||
chart: { | ||
width : options.width || defaultWidth, | ||
height: options.height || defaultHeight, | ||
backgroundColor: '#000' | ||
}, | ||
credits: { | ||
enabled: false | ||
}, | ||
title: { | ||
text: options.title, | ||
style : title_style | ||
}, | ||
xAxis: { | ||
title: { | ||
text: options.x_title || null, | ||
style : title_style, | ||
margin: 20 | ||
}, | ||
categories: options.x_labels, | ||
labels : { | ||
enabled: options.x_labels ? true : false | ||
}, | ||
tickLength: 0 | ||
}, | ||
yAxis: { | ||
startOnTick: false, | ||
title: { | ||
text: options.y_title || null, | ||
style : title_style, | ||
margin: 20 | ||
}, | ||
plotLines: yPlotLines, | ||
min : options.include_x_axis | ||
? 0 | ||
: options.range | ||
? options.range.min | ||
: null, | ||
max : options.range ? options.range.max : null, | ||
gridLineDashStyle : 'ShortDash', | ||
gridLineColor: '#DDD', | ||
tickLength: 0 | ||
}, | ||
legend: { | ||
itemStyle : { | ||
color : '#FFFFFF' | ||
}, | ||
layout: 'vertical', | ||
align: 'left', | ||
verticalAlign: 'top', | ||
y: 50, | ||
borderWidth: 0 | ||
}, | ||
labels : { | ||
style : { | ||
color: '#FFFFFF' | ||
} | ||
}, | ||
series: this._data | ||
}; | ||
|
||
for(let i = 0; i < chart.series.length; i++) { | ||
chart.series[i].legendIndex = chart.series.length - i; | ||
chart.series[i].index = chart.series.length - i; | ||
} | ||
|
||
if (this.renderer) { | ||
chart = this.renderer(options, chart); | ||
} | ||
|
||
Highcharts.chart(elem, chart); | ||
|
||
return ''; | ||
} | ||
|
||
#rgba(rgb, a) { | ||
return 'rgba(' + rgb.join(',') + ',' + a + ')'; | ||
} | ||
} | ||
|
||
class _Line extends Chart { | ||
constructor(...args) { | ||
super(...args); | ||
this.renderer = (options, chart) => { | ||
chart.chart.type = toJs(options.fill) ? 'area' : 'line'; | ||
return chart; | ||
}; | ||
} | ||
} | ||
export const Line = () => new _Line(); | ||
|
||
class _StackedLine extends Chart { | ||
constructor(...args) { | ||
super(...args); | ||
this.renderer = (options, chart) => { | ||
chart.chart.type = toJs(options.fill) ? 'area' : 'line'; | ||
chart.plotOptions = { | ||
area : { | ||
stacking : 'percent' | ||
}, | ||
series : { | ||
stacking : 'percent' | ||
} | ||
}; | ||
return chart; | ||
}; | ||
} | ||
} | ||
export const StackedLine = () => new _StackedLine(); | ||
|
||
class _Bar extends Chart { | ||
constructor(...args) { | ||
super(...args); | ||
this.renderer = (options, chart) => { | ||
chart.chart.type = 'column'; | ||
return chart; | ||
}; | ||
} | ||
} | ||
export const Bar = () => new _Bar(); | ||
|
||
class _StackedBar extends Chart { | ||
constructor(...args) { | ||
super(...args); | ||
this.renderer = (options, chart) => { | ||
chart.chart.type = 'column'; | ||
chart.plotOptions = { | ||
column : { | ||
stacking: 'percent' | ||
} | ||
}; | ||
return chart; | ||
}; | ||
} | ||
} | ||
export const StackedBar = () => new _StackedBar(); | ||
|
||
class _HorizontalBar extends Chart { | ||
constructor(...args) { | ||
super(...args); | ||
this.renderer = (options, chart) => { | ||
chart.chart.type = 'bar'; | ||
return chart; | ||
}; | ||
} | ||
} | ||
export const HorizontalBar = () => new _HorizontalBar(); | ||
|
||
class _StackedHorizontalBar extends Chart { | ||
constructor(...args) { | ||
super(...args); | ||
this.renderer = (options, chart) => { | ||
chart.chart.type = 'bar'; | ||
chart.plotOptions = { | ||
bar : { | ||
stacking: 'percent' | ||
} | ||
}; | ||
return chart; | ||
}; | ||
} | ||
} | ||
export const StackedHorizontalBar = () => new _StackedHorizontalBar(); | ||
|
||
class _XY extends Chart { | ||
constructor(...args) { | ||
super(...args); | ||
this.renderer = (options, chart) => { | ||
if (toJs(options.stroke) === false) { | ||
chart.chart.type = 'scatter' | ||
} | ||
else { | ||
chart.chart.type = toJs(options.fill) ? 'area' : 'line'; | ||
} | ||
chart.xAxis.labels.enabled = true; | ||
|
||
return chart; | ||
}; | ||
} | ||
} | ||
export const XY = () => new _XY(); | ||
|
||
class _Radar extends Chart { | ||
constructor(...args) { | ||
super(...args); | ||
this.renderer = (options, chart) => { | ||
chart.chart.polar = true; | ||
chart.chart.type = 'line'; | ||
chart.xAxis = { | ||
categories: toJs(options.x_labels), | ||
tickmarkPlacement: 'on', | ||
lineWidth: 0 | ||
} | ||
chart.yAxis = { | ||
gridLineInterpolation: 'polygon', | ||
lineWidth: 0, | ||
min: 0, | ||
gridLineDashStyle : 'ShortDash', | ||
gridLineColor: '#DDD' | ||
} | ||
for(let i = 0; i < chart.series.length; i++) { | ||
chart.series[i].pointPlacement = 'on'; | ||
} | ||
|
||
return chart; | ||
}; | ||
} | ||
} | ||
export const Radar = () => new _Radar(); | ||
|
||
class _Pie extends Chart { | ||
constructor(...args) { | ||
super(...args); | ||
this.renderer = (options, chart) => { | ||
chart.chart.type = 'pie'; | ||
const slices = []; | ||
const breakdown = []; | ||
const useBreakdown = false; | ||
for(let i = 0; i < chart.series.length; i++) { | ||
const slice = chart.series[i]; | ||
if (slice.data.length === 1) { | ||
slices.unshift({ | ||
name : slice.name, | ||
color : slice.color, | ||
borderColor : slice.color, | ||
legendIndex : slice.legendIndex, | ||
y : slice.data[0] | ||
}); | ||
breakdown.unshift({ | ||
name : slice.name, | ||
color : slice.color, | ||
borderColor : slice.color, | ||
y : slice.data[0] | ||
}); | ||
} | ||
else { | ||
useBreakdown = true; | ||
const sum = 0; | ||
const maxDecimal = 0; | ||
for(let j = 0; j < slice.data.length; j++) { | ||
const parts = slice.data[j].toString().split('.'); | ||
maxDecimal = Math.max(maxDecimal, parts[1] ? parts[1].length : 0); | ||
sum += slice.data[j]; | ||
breakdown.unshift({ | ||
name: slice.name, | ||
color: 'rgba(0,0,0,0)', | ||
borderColor : slice.color, | ||
y: slice.data[j] | ||
}); | ||
} | ||
slices.unshift({ | ||
name : slice.name, | ||
color : slice.color, | ||
borderColor : slice.color, | ||
legendIndex : slice.legendIndex, | ||
y : parseFloat(sum.toFixed(maxDecimal)) | ||
}); | ||
} | ||
} | ||
chart.tooltip = { | ||
formatter: function() { | ||
return this.key + ': ' + this.y; | ||
} | ||
}; | ||
chart.plotOptions = { | ||
pie: { | ||
allowPointSelect: !useBreakdown, | ||
cursor: useBreakdown ? null : 'pointer', | ||
shadow: false, | ||
center: ['50%', '50%'], | ||
dataLabels: { | ||
enabled: false | ||
} | ||
} | ||
}; | ||
chart.series = [{ | ||
name: ' ', | ||
data: slices, | ||
showInLegend: true | ||
}]; | ||
if (useBreakdown) { | ||
chart.series.push({ | ||
name: ' ', | ||
data: breakdown, | ||
innerSize: '90%', | ||
showInLegend: false | ||
}); | ||
} | ||
return chart; | ||
}; | ||
} | ||
} | ||
export const Pie = () => new _Pie(); |