@vue-pivottable/subtotal-renderer 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +295 -0
- package/dist/core.js +157 -0
- package/dist/core.mjs +157 -0
- package/dist/index.js +443 -0
- package/dist/index.mjs +444 -0
- package/dist/vue2.js +452 -0
- package/dist/vue2.mjs +453 -0
- package/package.json +56 -0
- package/types/index.d.ts +141 -0
package/dist/vue2.mjs
ADDED
|
@@ -0,0 +1,453 @@
|
|
|
1
|
+
import { createSubtotalAggregatorGetter } from "./core.mjs";
|
|
2
|
+
import { generateColKeysWithSubtotals, generateRowKeysWithSubtotals, getSubtotalKeys, isSubtotalKey } from "./core.mjs";
|
|
3
|
+
function redColorScaleGenerator(values) {
|
|
4
|
+
const min = Math.min.apply(Math, values);
|
|
5
|
+
const max = Math.max.apply(Math, values);
|
|
6
|
+
return (x) => {
|
|
7
|
+
const nonRed = 255 - Math.round(255 * (x - min) / (max - min));
|
|
8
|
+
return { backgroundColor: `rgb(255,${nonRed},${nonRed})` };
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
let _PivotData = null;
|
|
12
|
+
function createSubtotalRenderers(PivotData) {
|
|
13
|
+
_PivotData = PivotData;
|
|
14
|
+
return {
|
|
15
|
+
"Subtotal Table": makeSubtotalRenderer({ name: "vue-subtotal-table" })
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
function generateRowKeysWithSubtotals2(rowKeys, depth) {
|
|
19
|
+
if (depth <= 1) {
|
|
20
|
+
return rowKeys.map((key) => ({ key, isSubtotal: false, level: key.length }));
|
|
21
|
+
}
|
|
22
|
+
const result = [];
|
|
23
|
+
for (let i = 0; i < rowKeys.length; i++) {
|
|
24
|
+
const rowKey = rowKeys[i];
|
|
25
|
+
const nextRowKey = rowKeys[i + 1];
|
|
26
|
+
result.push({ key: rowKey, isSubtotal: false, level: rowKey.length });
|
|
27
|
+
for (let level = depth - 1; level >= 1; level--) {
|
|
28
|
+
const currentPrefix = rowKey.slice(0, level);
|
|
29
|
+
const nextPrefix = nextRowKey ? nextRowKey.slice(0, level) : null;
|
|
30
|
+
const levelChanges = !nextPrefix || JSON.stringify(currentPrefix) !== JSON.stringify(nextPrefix);
|
|
31
|
+
if (levelChanges) {
|
|
32
|
+
result.push({
|
|
33
|
+
key: currentPrefix,
|
|
34
|
+
isSubtotal: true,
|
|
35
|
+
level,
|
|
36
|
+
subtotalLabel: `${currentPrefix[level - 1]} Subtotal`
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return result;
|
|
42
|
+
}
|
|
43
|
+
function spanSize(rowItems, i, j) {
|
|
44
|
+
const arr = rowItems.map((item) => item.key);
|
|
45
|
+
if (rowItems[i].isSubtotal) {
|
|
46
|
+
return 1;
|
|
47
|
+
}
|
|
48
|
+
if (i !== 0 && !rowItems[i - 1].isSubtotal) {
|
|
49
|
+
let asPrevious = true;
|
|
50
|
+
for (let x = 0; x <= j; x++) {
|
|
51
|
+
if (arr[i - 1][x] !== arr[i][x]) {
|
|
52
|
+
asPrevious = false;
|
|
53
|
+
break;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
if (asPrevious) {
|
|
57
|
+
return -1;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
let len = 0;
|
|
61
|
+
while (i + len < arr.length) {
|
|
62
|
+
if (rowItems[i + len].isSubtotal)
|
|
63
|
+
break;
|
|
64
|
+
let same = true;
|
|
65
|
+
for (let x = 0; x <= j; x++) {
|
|
66
|
+
if (arr[i][x] !== arr[i + len][x]) {
|
|
67
|
+
same = false;
|
|
68
|
+
break;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
if (!same)
|
|
72
|
+
break;
|
|
73
|
+
len++;
|
|
74
|
+
}
|
|
75
|
+
return len;
|
|
76
|
+
}
|
|
77
|
+
function colSpanSize(arr, i, j) {
|
|
78
|
+
if (i !== 0) {
|
|
79
|
+
let asPrevious = true;
|
|
80
|
+
for (let x = 0; x <= j; x++) {
|
|
81
|
+
if (arr[i - 1][x] !== arr[i][x]) {
|
|
82
|
+
asPrevious = false;
|
|
83
|
+
break;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
if (asPrevious) {
|
|
87
|
+
return -1;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
let len = 0;
|
|
91
|
+
while (i + len < arr.length) {
|
|
92
|
+
let same = true;
|
|
93
|
+
for (let x = 0; x <= j; x++) {
|
|
94
|
+
if (arr[i][x] !== arr[i + len][x]) {
|
|
95
|
+
same = false;
|
|
96
|
+
break;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
if (!same)
|
|
100
|
+
break;
|
|
101
|
+
len++;
|
|
102
|
+
}
|
|
103
|
+
return len;
|
|
104
|
+
}
|
|
105
|
+
function makeSubtotalRenderer(opts = {}) {
|
|
106
|
+
return {
|
|
107
|
+
name: opts.name || "vue-subtotal-table",
|
|
108
|
+
props: {
|
|
109
|
+
data: {
|
|
110
|
+
type: [Array, Object, Function],
|
|
111
|
+
required: true
|
|
112
|
+
},
|
|
113
|
+
aggregators: Object,
|
|
114
|
+
aggregatorName: {
|
|
115
|
+
type: String,
|
|
116
|
+
default: "Count"
|
|
117
|
+
},
|
|
118
|
+
cols: {
|
|
119
|
+
type: Array,
|
|
120
|
+
default: () => []
|
|
121
|
+
},
|
|
122
|
+
rows: {
|
|
123
|
+
type: Array,
|
|
124
|
+
default: () => []
|
|
125
|
+
},
|
|
126
|
+
vals: {
|
|
127
|
+
type: Array,
|
|
128
|
+
default: () => []
|
|
129
|
+
},
|
|
130
|
+
valueFilter: {
|
|
131
|
+
type: Object,
|
|
132
|
+
default: () => ({})
|
|
133
|
+
},
|
|
134
|
+
sorters: {
|
|
135
|
+
type: [Function, Object],
|
|
136
|
+
default: () => ({})
|
|
137
|
+
},
|
|
138
|
+
derivedAttributes: {
|
|
139
|
+
type: [Function, Object],
|
|
140
|
+
default: () => ({})
|
|
141
|
+
},
|
|
142
|
+
rowOrder: {
|
|
143
|
+
type: String,
|
|
144
|
+
default: "key_a_to_z"
|
|
145
|
+
},
|
|
146
|
+
colOrder: {
|
|
147
|
+
type: String,
|
|
148
|
+
default: "key_a_to_z"
|
|
149
|
+
},
|
|
150
|
+
tableColorScaleGenerator: {
|
|
151
|
+
type: Function,
|
|
152
|
+
default: redColorScaleGenerator
|
|
153
|
+
},
|
|
154
|
+
tableOptions: {
|
|
155
|
+
type: Object,
|
|
156
|
+
default: () => ({})
|
|
157
|
+
},
|
|
158
|
+
localeStrings: {
|
|
159
|
+
type: Object,
|
|
160
|
+
default: () => ({
|
|
161
|
+
totals: "Totals"
|
|
162
|
+
})
|
|
163
|
+
},
|
|
164
|
+
rowTotal: {
|
|
165
|
+
type: Boolean,
|
|
166
|
+
default: true
|
|
167
|
+
},
|
|
168
|
+
colTotal: {
|
|
169
|
+
type: Boolean,
|
|
170
|
+
default: true
|
|
171
|
+
},
|
|
172
|
+
labels: {
|
|
173
|
+
type: Object,
|
|
174
|
+
default: () => ({})
|
|
175
|
+
},
|
|
176
|
+
// Subtotal specific props
|
|
177
|
+
subtotalOptions: {
|
|
178
|
+
type: Object,
|
|
179
|
+
default: () => ({
|
|
180
|
+
colSubtotalDisplay: {
|
|
181
|
+
displayOnTop: false,
|
|
182
|
+
enabled: true,
|
|
183
|
+
hideOnExpand: false
|
|
184
|
+
},
|
|
185
|
+
rowSubtotalDisplay: {
|
|
186
|
+
displayOnTop: false,
|
|
187
|
+
enabled: true,
|
|
188
|
+
hideOnExpand: false
|
|
189
|
+
},
|
|
190
|
+
arrowCollapsed: "▶",
|
|
191
|
+
arrowExpanded: "▼"
|
|
192
|
+
})
|
|
193
|
+
}
|
|
194
|
+
},
|
|
195
|
+
data() {
|
|
196
|
+
return {
|
|
197
|
+
collapsedRowKeys: /* @__PURE__ */ new Set(),
|
|
198
|
+
collapsedColKeys: /* @__PURE__ */ new Set()
|
|
199
|
+
};
|
|
200
|
+
},
|
|
201
|
+
methods: {
|
|
202
|
+
toggleRowCollapse(keyStr) {
|
|
203
|
+
if (this.collapsedRowKeys.has(keyStr)) {
|
|
204
|
+
this.collapsedRowKeys.delete(keyStr);
|
|
205
|
+
} else {
|
|
206
|
+
this.collapsedRowKeys.add(keyStr);
|
|
207
|
+
}
|
|
208
|
+
this.collapsedRowKeys = new Set(this.collapsedRowKeys);
|
|
209
|
+
this.$forceUpdate();
|
|
210
|
+
},
|
|
211
|
+
toggleColCollapse(keyStr) {
|
|
212
|
+
if (this.collapsedColKeys.has(keyStr)) {
|
|
213
|
+
this.collapsedColKeys.delete(keyStr);
|
|
214
|
+
} else {
|
|
215
|
+
this.collapsedColKeys.add(keyStr);
|
|
216
|
+
}
|
|
217
|
+
this.collapsedColKeys = new Set(this.collapsedColKeys);
|
|
218
|
+
this.$forceUpdate();
|
|
219
|
+
},
|
|
220
|
+
applyLabel(attr, value) {
|
|
221
|
+
if (this.labels && typeof this.labels[attr] === "function") {
|
|
222
|
+
return this.labels[attr](value);
|
|
223
|
+
}
|
|
224
|
+
return value;
|
|
225
|
+
}
|
|
226
|
+
},
|
|
227
|
+
render(h) {
|
|
228
|
+
if (!_PivotData) {
|
|
229
|
+
console.error("PivotData not initialized. Please use createSubtotalRenderers(PivotData) first.");
|
|
230
|
+
return h("div", "Error: PivotData not initialized. Use createSubtotalRenderers(PivotData).");
|
|
231
|
+
}
|
|
232
|
+
let pivotData;
|
|
233
|
+
try {
|
|
234
|
+
pivotData = new _PivotData(this.$props);
|
|
235
|
+
} catch (error) {
|
|
236
|
+
console.error(error);
|
|
237
|
+
return h("span", "Error rendering pivot table");
|
|
238
|
+
}
|
|
239
|
+
const colAttrs = pivotData.props.cols;
|
|
240
|
+
const rowAttrs = pivotData.props.rows;
|
|
241
|
+
const rowKeys = pivotData.getRowKeys();
|
|
242
|
+
const colKeys = pivotData.getColKeys();
|
|
243
|
+
const grandTotalAggregator = pivotData.getAggregator([], []);
|
|
244
|
+
const getAggregator = createSubtotalAggregatorGetter(pivotData);
|
|
245
|
+
const rowKeysWithSubtotals = generateRowKeysWithSubtotals2(rowKeys, rowAttrs.length);
|
|
246
|
+
const getClickHandler = (value, rowValues, colValues) => {
|
|
247
|
+
if (this.tableOptions && this.tableOptions.clickCallback) {
|
|
248
|
+
const filters = {};
|
|
249
|
+
colAttrs.forEach((attr, i) => {
|
|
250
|
+
if (colValues[i] !== void 0 && colValues[i] !== null) {
|
|
251
|
+
filters[attr] = colValues[i];
|
|
252
|
+
}
|
|
253
|
+
});
|
|
254
|
+
rowAttrs.forEach((attr, i) => {
|
|
255
|
+
if (rowValues[i] !== void 0 && rowValues[i] !== null) {
|
|
256
|
+
filters[attr] = rowValues[i];
|
|
257
|
+
}
|
|
258
|
+
});
|
|
259
|
+
return (e) => this.tableOptions.clickCallback(e, value, filters, pivotData);
|
|
260
|
+
}
|
|
261
|
+
return null;
|
|
262
|
+
};
|
|
263
|
+
const renderHeader = () => {
|
|
264
|
+
const headerRows = [];
|
|
265
|
+
colAttrs.forEach((c, j) => {
|
|
266
|
+
const cells = [];
|
|
267
|
+
if (j === 0 && rowAttrs.length !== 0) {
|
|
268
|
+
cells.push(h("th", {
|
|
269
|
+
attrs: {
|
|
270
|
+
colSpan: rowAttrs.length,
|
|
271
|
+
rowSpan: colAttrs.length
|
|
272
|
+
},
|
|
273
|
+
style: {
|
|
274
|
+
backgroundColor: "#f5f5f5"
|
|
275
|
+
}
|
|
276
|
+
}));
|
|
277
|
+
}
|
|
278
|
+
cells.push(h("th", { class: "pvtAxisLabel" }, c));
|
|
279
|
+
colKeys.forEach((colKey, i) => {
|
|
280
|
+
const colSpan = colSpanSize(colKeys, i, j);
|
|
281
|
+
if (colSpan !== -1) {
|
|
282
|
+
const label = this.applyLabel(colAttrs[j], colKey[j]);
|
|
283
|
+
cells.push(h("th", {
|
|
284
|
+
class: "pvtColLabel",
|
|
285
|
+
key: `colKey${i}-${j}`,
|
|
286
|
+
attrs: {
|
|
287
|
+
colSpan,
|
|
288
|
+
rowSpan: j === colAttrs.length - 1 && rowAttrs.length !== 0 ? 2 : 1
|
|
289
|
+
}
|
|
290
|
+
}, label));
|
|
291
|
+
}
|
|
292
|
+
});
|
|
293
|
+
if (j === 0 && this.rowTotal) {
|
|
294
|
+
cells.push(h("th", {
|
|
295
|
+
class: "pvtTotalLabel",
|
|
296
|
+
attrs: {
|
|
297
|
+
rowSpan: colAttrs.length + (rowAttrs.length === 0 ? 0 : 1)
|
|
298
|
+
}
|
|
299
|
+
}, this.localeStrings.totals));
|
|
300
|
+
}
|
|
301
|
+
headerRows.push(h("tr", { key: `colAttr${j}` }, cells));
|
|
302
|
+
});
|
|
303
|
+
if (rowAttrs.length !== 0) {
|
|
304
|
+
const cells = rowAttrs.map((r, i) => h("th", {
|
|
305
|
+
class: "pvtAxisLabel",
|
|
306
|
+
key: `rowAttr${i}`
|
|
307
|
+
}, r));
|
|
308
|
+
if (colAttrs.length === 0 && this.rowTotal) {
|
|
309
|
+
cells.push(h("th", { class: "pvtTotalLabel" }, this.localeStrings.totals));
|
|
310
|
+
}
|
|
311
|
+
headerRows.push(h("tr", cells));
|
|
312
|
+
}
|
|
313
|
+
return h("thead", headerRows);
|
|
314
|
+
};
|
|
315
|
+
const renderBody = () => {
|
|
316
|
+
const bodyRows = [];
|
|
317
|
+
rowKeysWithSubtotals.forEach((rowItem, i) => {
|
|
318
|
+
const { key: rowKey, isSubtotal, level, subtotalLabel } = rowItem;
|
|
319
|
+
const cells = [];
|
|
320
|
+
if (isSubtotal) {
|
|
321
|
+
const subtotalText = subtotalLabel || `${rowKey[rowKey.length - 1]} Subtotal`;
|
|
322
|
+
cells.push(h("th", {
|
|
323
|
+
class: "pvtRowLabel pvtSubtotalLabel",
|
|
324
|
+
attrs: {
|
|
325
|
+
colSpan: rowAttrs.length + (colAttrs.length !== 0 ? 1 : 0)
|
|
326
|
+
},
|
|
327
|
+
style: {
|
|
328
|
+
fontWeight: "bold",
|
|
329
|
+
backgroundColor: "#f0f0f0"
|
|
330
|
+
}
|
|
331
|
+
}, subtotalText));
|
|
332
|
+
colKeys.forEach((colKey, j) => {
|
|
333
|
+
const aggregator = getAggregator(rowKey, colKey);
|
|
334
|
+
const val = aggregator.value();
|
|
335
|
+
const formattedVal = aggregator.format ? aggregator.format(val) : val;
|
|
336
|
+
const clickHandler = getClickHandler(val, rowKey, colKey);
|
|
337
|
+
cells.push(h("td", {
|
|
338
|
+
class: "pvtVal pvtSubtotalVal",
|
|
339
|
+
key: `subtotal-val${i}-${j}`,
|
|
340
|
+
style: {
|
|
341
|
+
fontWeight: "bold",
|
|
342
|
+
backgroundColor: "#f0f0f0"
|
|
343
|
+
},
|
|
344
|
+
on: clickHandler ? { click: clickHandler } : {}
|
|
345
|
+
}, formattedVal));
|
|
346
|
+
});
|
|
347
|
+
if (this.rowTotal) {
|
|
348
|
+
const totalAggregator = getAggregator(rowKey, []);
|
|
349
|
+
const totalVal = totalAggregator.value();
|
|
350
|
+
const formattedTotal = totalAggregator.format ? totalAggregator.format(totalVal) : totalVal;
|
|
351
|
+
const clickHandler = getClickHandler(totalVal, rowKey, []);
|
|
352
|
+
cells.push(h("td", {
|
|
353
|
+
class: "pvtTotal pvtSubtotalTotal",
|
|
354
|
+
style: {
|
|
355
|
+
fontWeight: "bold",
|
|
356
|
+
backgroundColor: "#e0e0e0"
|
|
357
|
+
},
|
|
358
|
+
on: clickHandler ? { click: clickHandler } : {}
|
|
359
|
+
}, formattedTotal));
|
|
360
|
+
}
|
|
361
|
+
bodyRows.push(h("tr", {
|
|
362
|
+
key: `subtotal-row${i}`,
|
|
363
|
+
class: "pvtSubtotalRow"
|
|
364
|
+
}, cells));
|
|
365
|
+
} else {
|
|
366
|
+
rowKey.forEach((text, j) => {
|
|
367
|
+
const rowSpan = spanSize(rowKeysWithSubtotals, i, j);
|
|
368
|
+
if (rowSpan !== -1) {
|
|
369
|
+
const label = this.applyLabel(rowAttrs[j], text);
|
|
370
|
+
cells.push(h("th", {
|
|
371
|
+
class: "pvtRowLabel",
|
|
372
|
+
key: `rowLabel${i}-${j}`,
|
|
373
|
+
attrs: {
|
|
374
|
+
rowSpan,
|
|
375
|
+
colSpan: j === rowAttrs.length - 1 && colAttrs.length !== 0 ? 2 : 1
|
|
376
|
+
}
|
|
377
|
+
}, label));
|
|
378
|
+
}
|
|
379
|
+
});
|
|
380
|
+
colKeys.forEach((colKey, j) => {
|
|
381
|
+
const aggregator = getAggregator(rowKey, colKey);
|
|
382
|
+
const val = aggregator.value();
|
|
383
|
+
const formattedVal = aggregator.format ? aggregator.format(val) : val;
|
|
384
|
+
const clickHandler = getClickHandler(val, rowKey, colKey);
|
|
385
|
+
cells.push(h("td", {
|
|
386
|
+
class: "pvtVal",
|
|
387
|
+
key: `val${i}-${j}`,
|
|
388
|
+
on: clickHandler ? { click: clickHandler } : {}
|
|
389
|
+
}, formattedVal));
|
|
390
|
+
});
|
|
391
|
+
if (this.rowTotal) {
|
|
392
|
+
const totalAggregator = getAggregator(rowKey, []);
|
|
393
|
+
const totalVal = totalAggregator.value();
|
|
394
|
+
const formattedTotal = totalAggregator.format ? totalAggregator.format(totalVal) : totalVal;
|
|
395
|
+
const clickHandler = getClickHandler(totalVal, rowKey, []);
|
|
396
|
+
cells.push(h("td", {
|
|
397
|
+
class: "pvtTotal",
|
|
398
|
+
on: clickHandler ? { click: clickHandler } : {}
|
|
399
|
+
}, formattedTotal));
|
|
400
|
+
}
|
|
401
|
+
bodyRows.push(h("tr", { key: `row${i}` }, cells));
|
|
402
|
+
}
|
|
403
|
+
});
|
|
404
|
+
if (this.colTotal) {
|
|
405
|
+
const cells = [];
|
|
406
|
+
cells.push(h("th", {
|
|
407
|
+
class: "pvtTotalLabel",
|
|
408
|
+
attrs: {
|
|
409
|
+
colSpan: rowAttrs.length + (colAttrs.length === 0 ? 0 : 1)
|
|
410
|
+
}
|
|
411
|
+
}, this.localeStrings.totals));
|
|
412
|
+
colKeys.forEach((colKey, i) => {
|
|
413
|
+
const totalAggregator = getAggregator([], colKey);
|
|
414
|
+
const totalVal = totalAggregator.value();
|
|
415
|
+
const formattedTotal = totalAggregator.format ? totalAggregator.format(totalVal) : totalVal;
|
|
416
|
+
const clickHandler = getClickHandler(totalVal, [], colKey);
|
|
417
|
+
cells.push(h("td", {
|
|
418
|
+
class: "pvtTotal",
|
|
419
|
+
key: `colTotal${i}`,
|
|
420
|
+
on: clickHandler ? { click: clickHandler } : {}
|
|
421
|
+
}, formattedTotal));
|
|
422
|
+
});
|
|
423
|
+
if (this.rowTotal) {
|
|
424
|
+
const clickHandler = getClickHandler(grandTotalAggregator.value(), [], []);
|
|
425
|
+
cells.push(h("td", {
|
|
426
|
+
class: "pvtGrandTotal",
|
|
427
|
+
on: clickHandler ? { click: clickHandler } : {}
|
|
428
|
+
}, grandTotalAggregator.format(grandTotalAggregator.value())));
|
|
429
|
+
}
|
|
430
|
+
bodyRows.push(h("tr", cells));
|
|
431
|
+
}
|
|
432
|
+
return h("tbody", bodyRows);
|
|
433
|
+
};
|
|
434
|
+
return h("table", { class: "pvtTable" }, [
|
|
435
|
+
renderHeader(),
|
|
436
|
+
renderBody()
|
|
437
|
+
]);
|
|
438
|
+
}
|
|
439
|
+
};
|
|
440
|
+
}
|
|
441
|
+
const SubtotalRenderers = {
|
|
442
|
+
"Subtotal Table": makeSubtotalRenderer({ name: "vue-subtotal-table" })
|
|
443
|
+
};
|
|
444
|
+
export {
|
|
445
|
+
SubtotalRenderers,
|
|
446
|
+
createSubtotalAggregatorGetter,
|
|
447
|
+
createSubtotalRenderers,
|
|
448
|
+
SubtotalRenderers as default,
|
|
449
|
+
generateColKeysWithSubtotals,
|
|
450
|
+
generateRowKeysWithSubtotals,
|
|
451
|
+
getSubtotalKeys,
|
|
452
|
+
isSubtotalKey
|
|
453
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@vue-pivottable/subtotal-renderer",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Subtotal renderer for vue-pivottable with expand/collapse support. Supports Vue 2 and Vue 3.",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"module": "dist/index.mjs",
|
|
7
|
+
"types": "types/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": "./dist/index.mjs",
|
|
11
|
+
"require": "./dist/index.js",
|
|
12
|
+
"types": "./types/index.d.ts"
|
|
13
|
+
},
|
|
14
|
+
"./vue2": {
|
|
15
|
+
"import": "./dist/vue2.mjs",
|
|
16
|
+
"require": "./dist/vue2.js",
|
|
17
|
+
"types": "./types/index.d.ts"
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"files": [
|
|
21
|
+
"dist",
|
|
22
|
+
"types"
|
|
23
|
+
],
|
|
24
|
+
"scripts": {
|
|
25
|
+
"build": "vite build",
|
|
26
|
+
"dev": "vite"
|
|
27
|
+
},
|
|
28
|
+
"keywords": [
|
|
29
|
+
"vue",
|
|
30
|
+
"vue2",
|
|
31
|
+
"vue3",
|
|
32
|
+
"pivottable",
|
|
33
|
+
"pivot",
|
|
34
|
+
"subtotal",
|
|
35
|
+
"renderer",
|
|
36
|
+
"expand",
|
|
37
|
+
"collapse"
|
|
38
|
+
],
|
|
39
|
+
"author": "Seungwoo321",
|
|
40
|
+
"license": "MIT",
|
|
41
|
+
"repository": {
|
|
42
|
+
"type": "git",
|
|
43
|
+
"url": "https://github.com/vue-pivottable/subtotal-renderer.git"
|
|
44
|
+
},
|
|
45
|
+
"peerDependencies": {
|
|
46
|
+
"vue": "^2.6.0 || ^3.0.0",
|
|
47
|
+
"vue-pivottable": ">=0.4.0"
|
|
48
|
+
},
|
|
49
|
+
"devDependencies": {
|
|
50
|
+
"release-it": "^17.0.0",
|
|
51
|
+
"vite": "^4.5.0"
|
|
52
|
+
},
|
|
53
|
+
"publishConfig": {
|
|
54
|
+
"access": "public"
|
|
55
|
+
}
|
|
56
|
+
}
|
package/types/index.d.ts
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type definitions for @vue-pivottable/subtotal-renderer
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { Component } from 'vue'
|
|
6
|
+
|
|
7
|
+
// ============================================================================
|
|
8
|
+
// Subtotal Options
|
|
9
|
+
// ============================================================================
|
|
10
|
+
|
|
11
|
+
export interface SubtotalDisplayOptions {
|
|
12
|
+
/** Display subtotals on top of the group (default: false) */
|
|
13
|
+
displayOnTop?: boolean
|
|
14
|
+
/** Enable subtotals (default: true) */
|
|
15
|
+
enabled?: boolean
|
|
16
|
+
/** Hide subtotals when group is expanded (default: false) */
|
|
17
|
+
hideOnExpand?: boolean
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface SubtotalOptions {
|
|
21
|
+
/** Column subtotal display options */
|
|
22
|
+
colSubtotalDisplay?: SubtotalDisplayOptions
|
|
23
|
+
/** Row subtotal display options */
|
|
24
|
+
rowSubtotalDisplay?: SubtotalDisplayOptions
|
|
25
|
+
/** Arrow character for collapsed state (default: '▶') */
|
|
26
|
+
arrowCollapsed?: string
|
|
27
|
+
/** Arrow character for expanded state (default: '▼') */
|
|
28
|
+
arrowExpanded?: string
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// ============================================================================
|
|
32
|
+
// Core Functions
|
|
33
|
+
// ============================================================================
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Generate all possible subtotal key combinations for a given key
|
|
37
|
+
* @param key - The full key array
|
|
38
|
+
* @returns Array of partial keys for subtotals
|
|
39
|
+
*/
|
|
40
|
+
export function getSubtotalKeys(key: string[]): string[][]
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Check if a key represents a subtotal row/column
|
|
44
|
+
* @param key - The key to check
|
|
45
|
+
* @param maxDepth - Maximum depth (number of attributes)
|
|
46
|
+
*/
|
|
47
|
+
export function isSubtotalKey(key: string[], maxDepth: number): boolean
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Row/Column key with subtotal information
|
|
51
|
+
*/
|
|
52
|
+
export interface SubtotalKeyInfo {
|
|
53
|
+
key: string[]
|
|
54
|
+
isSubtotal: boolean
|
|
55
|
+
level: number
|
|
56
|
+
isCollapsed: boolean
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Generate row keys with subtotals inserted
|
|
61
|
+
* @param rowKeys - Original row keys from PivotData
|
|
62
|
+
* @param rowAttrsCount - Number of row attributes
|
|
63
|
+
* @param collapsedKeys - Set of collapsed key strings
|
|
64
|
+
*/
|
|
65
|
+
export function generateRowKeysWithSubtotals(
|
|
66
|
+
rowKeys: string[][],
|
|
67
|
+
rowAttrsCount: number,
|
|
68
|
+
collapsedKeys?: Set<string>
|
|
69
|
+
): SubtotalKeyInfo[]
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Generate column keys with subtotals inserted
|
|
73
|
+
* @param colKeys - Original column keys from PivotData
|
|
74
|
+
* @param colAttrsCount - Number of column attributes
|
|
75
|
+
* @param collapsedKeys - Set of collapsed key strings
|
|
76
|
+
*/
|
|
77
|
+
export function generateColKeysWithSubtotals(
|
|
78
|
+
colKeys: string[][],
|
|
79
|
+
colAttrsCount: number,
|
|
80
|
+
collapsedKeys?: Set<string>
|
|
81
|
+
): SubtotalKeyInfo[]
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Create a subtotal-aware aggregator getter
|
|
85
|
+
* @param pivotData - The PivotData instance
|
|
86
|
+
*/
|
|
87
|
+
export function createSubtotalAggregatorGetter(pivotData: any): (
|
|
88
|
+
rowKey: string[],
|
|
89
|
+
colKey: string[]
|
|
90
|
+
) => { value: () => any; format: (val: any) => string }
|
|
91
|
+
|
|
92
|
+
// ============================================================================
|
|
93
|
+
// Renderer Props
|
|
94
|
+
// ============================================================================
|
|
95
|
+
|
|
96
|
+
export interface SubtotalRendererProps {
|
|
97
|
+
data: any[] | object | Function
|
|
98
|
+
aggregators?: Record<string, Function>
|
|
99
|
+
aggregatorName?: string
|
|
100
|
+
cols?: string[]
|
|
101
|
+
rows?: string[]
|
|
102
|
+
vals?: string[]
|
|
103
|
+
valueFilter?: Record<string, Record<string, boolean>>
|
|
104
|
+
sorters?: Function | Record<string, Function>
|
|
105
|
+
derivedAttributes?: Function | Record<string, Function>
|
|
106
|
+
rowOrder?: 'key_a_to_z' | 'value_a_to_z' | 'value_z_to_a'
|
|
107
|
+
colOrder?: 'key_a_to_z' | 'value_a_to_z' | 'value_z_to_a'
|
|
108
|
+
tableColorScaleGenerator?: (values: number[]) => (value: number) => { backgroundColor: string }
|
|
109
|
+
tableOptions?: {
|
|
110
|
+
clickCallback?: (
|
|
111
|
+
event: MouseEvent,
|
|
112
|
+
value: any,
|
|
113
|
+
filters: Record<string, any>,
|
|
114
|
+
pivotData: any
|
|
115
|
+
) => void
|
|
116
|
+
}
|
|
117
|
+
localeStrings?: {
|
|
118
|
+
totals?: string
|
|
119
|
+
}
|
|
120
|
+
rowTotal?: boolean
|
|
121
|
+
colTotal?: boolean
|
|
122
|
+
labels?: Record<string, (value: any) => string>
|
|
123
|
+
subtotalOptions?: SubtotalOptions
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// ============================================================================
|
|
127
|
+
// Renderers
|
|
128
|
+
// ============================================================================
|
|
129
|
+
|
|
130
|
+
export interface SubtotalRendererComponent extends Component {
|
|
131
|
+
name: string
|
|
132
|
+
props: SubtotalRendererProps
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export interface SubtotalRenderersType {
|
|
136
|
+
'Subtotal Table': SubtotalRendererComponent
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export const SubtotalRenderers: SubtotalRenderersType
|
|
140
|
+
|
|
141
|
+
export default SubtotalRenderers
|