diff --git a/packages/ez-vue/src/components/Bars.tsx b/packages/ez-vue/src/components/Bars.tsx
index f62ea04..1844536 100644
--- a/packages/ez-vue/src/components/Bars.tsx
+++ b/packages/ez-vue/src/components/Bars.tsx
@@ -42,12 +42,22 @@ export default class Bars extends Vue {
}
render() {
- const { shapeData } = this;
+ const {
+ shapeData, chart, $scopedSlots, cartesianScale, colorScale,
+ } = this;
+ const { dimensions } = chart;
+
return (
- {shapeData.map((rectDatum) => (
-
- ))}
+ {$scopedSlots.default
+ ? $scopedSlots.default({
+ shapeData,
+ scales: { ...cartesianScale, colorScale },
+ dimensions,
+ })
+ : shapeData.map((rectDatum) => (
+
+ ))}
);
}
diff --git a/packages/ez-vue/src/components/StackedBars.tsx b/packages/ez-vue/src/components/StackedBars.tsx
new file mode 100644
index 0000000..ce2458d
--- /dev/null
+++ b/packages/ez-vue/src/components/StackedBars.tsx
@@ -0,0 +1,114 @@
+import Vue, { PropType } from 'vue';
+import Component from 'vue-class-component';
+import {
+ ChartContext,
+ Direction,
+ RectangleDatum,
+ ScaleLinearOrBand,
+} from 'eazychart-core/src/types';
+import { InjectReactive, Prop } from 'vue-property-decorator';
+import { ScaleOrdinal, scaleRectangleData } from 'eazychart-core/src';
+import Bar from '@/components/shapes/Bar';
+
+@Component({ components: { Bar } })
+export default class StackedBars extends Vue {
+ @InjectReactive('chart')
+ private chart!: ChartContext;
+
+ @InjectReactive('cartesianScale')
+ private cartesianScale!: {
+ xScale: ScaleLinearOrBand;
+ yScale: ScaleLinearOrBand;
+ };
+
+ @Prop({
+ type: String,
+ required: true,
+ })
+ private readonly singleDomainKey!: string;
+
+ @Prop({
+ type: Array,
+ required: true,
+ })
+ private readonly stackDomainKeys!: string[];
+
+ @Prop({
+ type: String as PropType,
+ required: true,
+ })
+ private readonly direction: Direction = Direction.VERTICAL;
+
+ get colorScale() {
+ return this.chart.getScale('colorScale') as ScaleOrdinal;
+ }
+
+ get scaledDataDict() {
+ return this.stackDomainKeys.reduce(
+ (acc: { [key: string]: RectangleDatum[] }, yDomainKey) => {
+ acc[yDomainKey] = scaleRectangleData(
+ this.chart.data,
+ this.singleDomainKey,
+ yDomainKey,
+ this.cartesianScale.xScale,
+ this.cartesianScale.yScale,
+ this.colorScale,
+ this.chart.dimensions,
+ this.chart.isRTL,
+ );
+ return acc;
+ },
+ {},
+ );
+ }
+
+ render() {
+ const {
+ scaledDataDict, chart, stackDomainKeys, colorScale, direction,
+ } = this;
+
+ return (
+
+ {chart.data.map((_datum, idx) => (
+ // The Domain keys still needs to be sorted.
+ // We create a bar for every data row
+ // Each bar is a stack bar where every element is a domain key.
+
+ {stackDomainKeys.map((yDomainKey, domainIdx) => {
+ const color = colorScale.scale(yDomainKey);
+ const scaledData = scaledDataDict[yDomainKey][idx];
+ // The first domain key will not be affected.
+ const previousRectWidth = domainIdx !== 0
+ ? scaledDataDict[stackDomainKeys[domainIdx - 1]][idx].width
+ : 0;
+ const previousRectHeight = domainIdx !== 0
+ ? scaledDataDict[stackDomainKeys[domainIdx - 1]][idx].height
+ : 0;
+ // The height or the width of the current bar will be computed depending
+ // to the orientaion
+ // the height will be currentDKHeight - previousDKHeight (same for the width)
+ const shapeDatum = {
+ ...scaledData,
+ width:
+ direction === Direction.HORIZONTAL
+ ? scaledData.width - previousRectWidth
+ : scaledData.width,
+ height:
+ direction === Direction.VERTICAL
+ ? scaledData.height - previousRectHeight
+ : scaledData.height,
+ };
+
+ return (
+
+ );
+ })}
+
+ ))}
+
+ );
+ }
+}
diff --git a/packages/ez-vue/src/components/scales/ColorScale.tsx b/packages/ez-vue/src/components/scales/ColorScale.tsx
index d3f561a..24c8cbe 100644
--- a/packages/ez-vue/src/components/scales/ColorScale.tsx
+++ b/packages/ez-vue/src/components/scales/ColorScale.tsx
@@ -24,7 +24,7 @@ export default class ColorScale extends Vue {
})
private readonly definition!: ScaleOrdinalDefinition;
- mounted() {
+ created() {
this.colorScale = this.defineScale();
this.chart.registerScale('colorScale', this.colorScale);
}
diff --git a/packages/ez-vue/src/recipes/column/ColumnChart.stories.tsx b/packages/ez-vue/src/recipes/column/ColumnChart.stories.tsx
index 2dd20c2..9514eb6 100644
--- a/packages/ez-vue/src/recipes/column/ColumnChart.stories.tsx
+++ b/packages/ez-vue/src/recipes/column/ColumnChart.stories.tsx
@@ -5,6 +5,7 @@ import {
} from 'eazychart-dev/storybook/data';
import ColumnChart from './ColumnChart';
import LineColumnChart from './LineColumnChart';
+import StackedColumnChart from './StackedColumnChart';
const meta: Meta = {
title: 'Vue/Column Chart',
@@ -38,6 +39,17 @@ const LineColumnTemplate: Story = (_args, { argTypes }) => ({
`,
});
+const StackedColumnTemplate: Story = (_args, { argTypes }) => ({
+ title: 'StackedColumn',
+ components: { StackedColumnChart, ChartWrapper },
+ props: Object.keys(argTypes),
+ template: `
+
+
+
+ `,
+});
+
// By passing using the Args format for exported stories,
// you can control the props for a component for reuse in a test
// https://storybook.js.org/docs/vue/workflows/unit-testing
@@ -84,3 +96,16 @@ const lineColumnArguments = {
};
LineColumn.args = lineColumnArguments;
+
+export const StackedColumn = StackedColumnTemplate.bind({});
+
+const StackedColumnArguments = {
+ ...defaultArguments,
+ yAxis: {
+ domainKeys: ['value', 'value1', 'value2'],
+ title: 'Temperature',
+ tickFormat: (d: number) => `${d}°`,
+ },
+};
+
+StackedColumn.args = StackedColumnArguments;
diff --git a/packages/ez-vue/src/recipes/column/StackedColumnChart.tsx b/packages/ez-vue/src/recipes/column/StackedColumnChart.tsx
new file mode 100644
index 0000000..c712a1a
--- /dev/null
+++ b/packages/ez-vue/src/recipes/column/StackedColumnChart.tsx
@@ -0,0 +1,212 @@
+import { PropType } from 'vue';
+import Component, { mixins } from 'vue-class-component';
+import {
+ AnimationOptions,
+ ChartPadding,
+ Direction,
+ Position,
+ RawData,
+ GridConfig,
+ AxisConfig,
+ Dimensions,
+ AxisConfigMulti,
+} from 'eazychart-core/src/types';
+import { Prop } from 'vue-property-decorator';
+import { ScaleBand, ScaleLinear } from 'eazychart-core/src';
+import Chart from '@/components/Chart';
+import Axis from '@/components/scales/Axis';
+import Legend from '@/components/addons/legend/Legend';
+import Tooltip from '@/components/addons/tooltip/Tooltip';
+import StackedBars from '@/components/StackedBars';
+import Grid from '@/components/scales/grid/Grid';
+import ColorScale from '@/components/scales/ColorScale';
+import CartesianScale from '@/components/scales/CartesianScale';
+import ToggleDomainKeyMixin from '@/lib/ToggleDomainKeyMixin';
+
+@Component({
+ components: {
+ Chart,
+ Grid,
+ StackedBars,
+ Axis,
+ Legend,
+ Tooltip,
+ },
+})
+export default class StackedColumnChart extends mixins(ToggleDomainKeyMixin) {
+ @Prop({
+ type: Array as PropType,
+ required: true,
+ })
+ private readonly data!: RawData;
+
+ @Prop({
+ type: Array,
+ default() {
+ return ['#339999', '#993399', '#333399'];
+ },
+ })
+ private readonly colors!: string[];
+
+ @Prop({
+ type: Object as PropType,
+ default() {
+ return {};
+ },
+ })
+ private readonly dimensions!: Partial;
+
+ @Prop({
+ type: Object as PropType,
+ default() {
+ return {
+ easing: 'easeBack',
+ duration: 400,
+ delay: 0,
+ };
+ },
+ })
+ private readonly animationOptions!: AnimationOptions;
+
+ @Prop({
+ type: Object as PropType,
+ default() {
+ return {
+ left: 150,
+ bottom: 100,
+ right: 150,
+ top: 100,
+ };
+ },
+ })
+ private readonly padding!: ChartPadding;
+
+ @Prop({
+ type: Object as PropType,
+ default() {
+ return {
+ directions: [Direction.HORIZONTAL, Direction.VERTICAL],
+ color: '#a8a8a8',
+ };
+ },
+ })
+ private readonly grid!: GridConfig;
+
+ @Prop({
+ type: Object as PropType>,
+ default() {
+ return {
+ domainKey: 'name',
+ };
+ },
+ })
+ private readonly xAxis!: AxisConfig;
+
+ @Prop({
+ type: Object as PropType>,
+ default() {
+ return {
+ domainKeys: ['value', 'value1', 'value2'],
+ };
+ },
+ })
+ private readonly yAxis!: AxisConfigMulti;
+
+ @Prop({
+ type: Boolean,
+ default() {
+ return false;
+ },
+ })
+ private readonly isRTL!: boolean;
+
+ getData(): RawData {
+ return this.data;
+ }
+
+ getDomainKeys(): string[] {
+ return this.yAxis.domainKeys;
+ }
+
+ render() {
+ const {
+ xAxis,
+ yAxis,
+ data,
+ colors,
+ padding,
+ animationOptions,
+ grid,
+ isRTL,
+ $scopedSlots,
+ dimensions,
+ activeDomain,
+ activeDomainKeys,
+ toggleDomainKey,
+ } = this;
+
+ const scopedSlots = {
+ Legend: $scopedSlots.Legend
+ ? $scopedSlots.Legend
+ : () => ,
+ Tooltip: $scopedSlots.Tooltip,
+ };
+ return (
+
+
+
+
+
+
+
+
+
+
+ );
+ }
+}