Skip to content

Commit 880f1ac

Browse files
committed
feature: add series, add categories, use xy-chart types
1 parent a19283e commit 880f1ac

File tree

9 files changed

+501
-5
lines changed

9 files changed

+501
-5
lines changed

src/helper/general-helper.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,4 @@ export class GeneralHelper {
1313
if (!object || typeof object !== 'object') return false;
1414
return !!Object.getOwnPropertyDescriptor(object, property);
1515
}
16-
}
16+
}

src/helper/modify-chart-pattern.ts

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
import {
2+
ChartData,
3+
ModificationPattern,
4+
ModificationPatternChildren,
5+
XYChartData,
6+
} from '../types/types';
7+
import { ModifyChart } from './modify-chart';
8+
9+
export class ModifyChartPattern extends ModifyChart {
10+
valuesPattern: any;
11+
12+
constructor(chart: XMLDocument, data: XYChartData | ChartData, valuesPattern, addCols?: any[]) {
13+
super(chart, data, addCols)
14+
this.valuesPattern = valuesPattern
15+
}
16+
17+
setChart() {
18+
this.setValues()
19+
this.setSeries()
20+
}
21+
22+
setValues() {
23+
this.data.categories.forEach((category, c) => {
24+
category.values.forEach((value, s) => {
25+
this.pattern(this.series(s, this.valuesPattern(this, category, value, c, s)));
26+
});
27+
});
28+
}
29+
30+
setSeries() {
31+
this.data.series.forEach((series, s) => {
32+
this.pattern(this.series(s, {
33+
...this.seriesId(s),
34+
...this.seriesLabel(series.label, s + this.addColsLength)
35+
}));
36+
});
37+
}
38+
39+
series = (
40+
index: number,
41+
children: ModificationPatternChildren,
42+
): ModificationPatternChildren => {
43+
return {
44+
'c:ser': {
45+
index: index,
46+
children: children,
47+
},
48+
};
49+
};
50+
51+
seriesId = (series: number): ModificationPatternChildren => {
52+
return {
53+
'c:idx': {
54+
modify: this.attribute('val', series),
55+
},
56+
'c:order': {
57+
modify: this.attribute('val', series+1),
58+
}
59+
};
60+
};
61+
62+
seriesLabel = (label: string, series: number): ModificationPatternChildren => {
63+
return {
64+
'c:f': {
65+
modify: this.range(series+1),
66+
},
67+
'c:v': {
68+
modify: this.text(label),
69+
},
70+
};
71+
};
72+
73+
defaultValues = (
74+
label: string,
75+
value: number,
76+
index: number,
77+
series: number,
78+
): ModificationPatternChildren => {
79+
return {
80+
'c:val': this.point(value, index, series+1),
81+
'c:cat': this.point(label, index, 0),
82+
};
83+
};
84+
85+
xyValues = (
86+
xValue: number,
87+
yValue: number,
88+
index: number,
89+
series: number,
90+
): ModificationPatternChildren => {
91+
return {
92+
'c:xVal': this.point(xValue, index, series+2),
93+
'c:yVal': this.point(yValue, index, 1),
94+
};
95+
};
96+
97+
point = (
98+
value: number | string,
99+
index: number,
100+
colId: number,
101+
): ModificationPattern => {
102+
return {
103+
children: {
104+
'c:pt': {
105+
index: index,
106+
modify: this.value(value, index),
107+
},
108+
'c:f': {
109+
modify: this.range(colId, this.height),
110+
},
111+
'c:ptCount': {
112+
modify: this.attribute('val', this.height),
113+
},
114+
},
115+
};
116+
};
117+
}

src/helper/modify-chart.ts

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
import {
2+
ChartData,
3+
ModificationPatternChildren, XYChartData,
4+
} from '../types/types';
5+
import { GeneralHelper } from './general-helper';
6+
import { XmlHelper } from './xml-helper';
7+
import StringIdGenerator from './string-id-generator';
8+
9+
export class ModifyChart {
10+
root: XMLDocument;
11+
StringIdGenerator: StringIdGenerator;
12+
data: XYChartData | ChartData;
13+
height: number;
14+
width: number;
15+
addCols: any[];
16+
addColsLength: number;
17+
18+
constructor(root: XMLDocument, data: XYChartData | ChartData, addCols?: any[]) {
19+
this.root = root
20+
this.StringIdGenerator = new StringIdGenerator('ABCDEFGHIJKLMNOPQRSTUVWXYZ')
21+
22+
this.data = data
23+
this.height = this.data.categories.length;
24+
this.addCols = GeneralHelper.arrayify(addCols);
25+
this.addColsLength = this.addCols.length;
26+
this.width = this.data.series.length + 1 + this.addColsLength;
27+
}
28+
29+
pattern(
30+
pattern: ModificationPatternChildren,
31+
root?: XMLDocument | Element
32+
): void {
33+
root = root || this.root
34+
35+
for (const tag in pattern) {
36+
const parentPattern = pattern[tag];
37+
const index = parentPattern.index || 0;
38+
this.assert(root.getElementsByTagName(tag), index)
39+
const element = root.getElementsByTagName(tag)[index];
40+
41+
if (GeneralHelper.propertyExists(parentPattern, 'modify')) {
42+
const modifies = GeneralHelper.arrayify(parentPattern.modify)
43+
Object.values(modifies).forEach(modify => modify(element))
44+
}
45+
46+
if (GeneralHelper.propertyExists(parentPattern, 'children')) {
47+
this.pattern(parentPattern.children, element);
48+
}
49+
}
50+
}
51+
52+
text = (label: string) => (element: Element): void => {
53+
element.firstChild.textContent = String(label);
54+
};
55+
56+
value = (value: number | string, index?: number) => (element: Element): void => {
57+
element.getElementsByTagName('c:v')[0].firstChild.textContent = String(
58+
value,
59+
);
60+
if(index !== undefined) {
61+
element.setAttribute('idx', String(index));
62+
}
63+
};
64+
65+
attribute = (attribute: string, value: string | number) => (
66+
element: Element,
67+
): void => {
68+
element.setAttribute(attribute, String(value));
69+
};
70+
71+
range = (series: number, length?: number) => (element: Element): void => {
72+
this.setRange(element, series, length);
73+
};
74+
75+
setRange(element: Element, colId: number, length?: number): void {
76+
const range = element.firstChild.textContent;
77+
const info = range.split('!');
78+
const spans = info[1].split(':');
79+
const start = spans[0].split('$');
80+
const startRow = Number(spans[0].split('$')[2]);
81+
const colLetter = this.StringIdGenerator.start(colId).next()
82+
83+
let endCell = ''
84+
if(length !== undefined) {
85+
const endRow = String(startRow + length - 1);
86+
endCell = `:$${colLetter}$${endRow}`;
87+
}
88+
89+
const newRange = `${info[0]}!$${colLetter}$${start[2]}${endCell}`;
90+
91+
element.firstChild.textContent = newRange;
92+
}
93+
94+
getSpanString(startColNumber: number, startRowNumber:number, cols:number, rows:number): string {
95+
const startColLetter = this.StringIdGenerator.start(startColNumber).next()
96+
const endColLetter = this.StringIdGenerator.start(startColNumber+cols).next()
97+
const endRowNumber = startRowNumber + rows
98+
return `${startColLetter}${startRowNumber}:${endColLetter}${endRowNumber}`
99+
}
100+
101+
getCellAddressString(c:number,r:number): string {
102+
const colLetter = this.StringIdGenerator.start(c).next()
103+
return `${colLetter}${r+1}`
104+
}
105+
106+
assert(collection: HTMLCollectionOf<Element>, index: number) {
107+
if (!collection[index]) {
108+
const tplNode = collection[collection.length - 1];
109+
const newChild = tplNode.cloneNode(true);
110+
XmlHelper.insertAfter(newChild, tplNode);
111+
}
112+
}
113+
}
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
import {
2+
ChartData,
3+
ModificationPatternChildren,
4+
Workbook,
5+
XYChartData,
6+
} from '../types/types';
7+
import { ModifyChart } from './modify-chart';
8+
9+
export class ModifyWorkbookPattern extends ModifyChart {
10+
sharedStrings: XMLDocument;
11+
table: any;
12+
13+
constructor(workbook: Workbook, data: XYChartData | ChartData, addCols?: any[]) {
14+
super(workbook.sheet, data, addCols)
15+
this.sharedStrings = workbook.sharedStrings
16+
this.table = workbook.table
17+
}
18+
19+
setWorkbook = () => {
20+
this.pattern(this.spanString())
21+
22+
this.data.categories.forEach((category, c) => {
23+
const r = c+1
24+
this.pattern(this.rowLabels(r, category.label));
25+
this.pattern(this.rowAttributes(r, r+1));
26+
27+
this.addCols.forEach(addCol => addCol(this, category, r))
28+
29+
category.values.forEach((xValue, s) => {
30+
this.pattern(this.rowValues(r, s+1+this.addColsLength, xValue));
31+
});
32+
});
33+
34+
this.pattern(this.rowAttributes(0, 1));
35+
this.data.series.forEach((series, s) => {
36+
this.pattern(this.colLabel(s+1+this.addColsLength, series.label))
37+
});
38+
}
39+
40+
colLabel = (c:number, label:string): ModificationPatternChildren => {
41+
this.setTableColumn(c, label)
42+
return {
43+
'row': {
44+
modify: this.attribute('spans', `1:${this.width}`),
45+
children: {
46+
'c': {
47+
index: c,
48+
modify: this.attribute('r', this.getCellAddressString(c,0)),
49+
children: this.sharedString(label)
50+
}
51+
}
52+
}
53+
}
54+
}
55+
56+
rowAttributes = (r: number, rowId: number): ModificationPatternChildren => {
57+
return {
58+
'row': {
59+
index: r,
60+
modify: [
61+
this.attribute('spans', `1:${this.width}`),
62+
this.attribute('r', String(rowId))
63+
]
64+
}
65+
}
66+
}
67+
68+
rowLabels = (r:number, label:string): ModificationPatternChildren => {
69+
return {
70+
'row': {
71+
index: r,
72+
children: {
73+
'c': {
74+
modify: this.attribute('r', this.getCellAddressString(0,r)),
75+
children: this.sharedString(label)
76+
}
77+
}
78+
}
79+
}
80+
}
81+
82+
rowValues = (r:number, c:number, value:number): ModificationPatternChildren => {
83+
return {
84+
'row': {
85+
index: r,
86+
children: {
87+
'c': {
88+
index: c,
89+
modify: this.attribute('r', this.getCellAddressString(c,r)),
90+
children: this.cellValue(value)
91+
}
92+
}
93+
}
94+
}
95+
}
96+
97+
spanString(): ModificationPatternChildren {
98+
return {
99+
'dimension': {
100+
modify: this.attribute('ref', this.getSpanString(0, 1, this.width - this.addColsLength, this.height))
101+
}
102+
}
103+
}
104+
105+
sharedString = (label: string): ModificationPatternChildren => {
106+
const stringId = this.appendSharedString(
107+
this.sharedStrings,
108+
label
109+
);
110+
return this.cellValue(stringId)
111+
};
112+
113+
cellValue = (value: number): ModificationPatternChildren => {
114+
return {
115+
'v': {
116+
modify: this.text(String(value))
117+
}
118+
}
119+
};
120+
121+
setTableColumn(c: number, label: string) {
122+
this.table.getElementsByTagName('table')[0]
123+
.setAttribute('ref', this.getSpanString(0, 1, this.width-1, this.height))
124+
125+
const tableColumns = this.table.getElementsByTagName('tableColumns')[0]
126+
tableColumns.setAttribute('count', this.width-1)
127+
128+
this.assert(tableColumns.getElementsByTagName('tableColumn') , c)
129+
130+
const tableColumn = tableColumns.getElementsByTagName('tableColumn')[c]
131+
tableColumn.setAttribute('id', c+1)
132+
tableColumn.setAttribute('name', label);
133+
}
134+
135+
appendSharedString(sharedStrings: Document, stringValue: string): number {
136+
const strings = sharedStrings.getElementsByTagName('sst')[0];
137+
const newLabel = sharedStrings.createTextNode(stringValue);
138+
const newText = sharedStrings.createElement('t');
139+
newText.appendChild(newLabel);
140+
141+
const newString = sharedStrings.createElement('si');
142+
newString.appendChild(newText);
143+
144+
strings.appendChild(newString);
145+
146+
return strings.getElementsByTagName('si').length - 1;
147+
}
148+
}

0 commit comments

Comments
 (0)