@vue-pivottable/multi-value-renderer 0.2.2 → 0.3.0-beta.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/dist/core.js +80 -3
- package/dist/core.mjs +80 -3
- package/dist/index.js +2 -6
- package/dist/index.mjs +3 -7
- package/dist/vue2.js +57 -2
- package/dist/vue2.mjs +56 -1
- package/dist/vue3.js +418 -3
- package/dist/vue3.mjs +418 -3
- package/package.json +1 -1
- package/dist/MultiValuePivotData.js +0 -81
- package/dist/MultiValueTableRenderer.js +0 -364
|
@@ -1,364 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
const vue = require("vue");
|
|
3
|
-
const vuePivottable = require("vue-pivottable");
|
|
4
|
-
const MultiValuePivotData = require("./MultiValuePivotData.js");
|
|
5
|
-
const { PivotData } = vuePivottable.PivotUtilities;
|
|
6
|
-
function redColorScaleGenerator(values) {
|
|
7
|
-
const numericValues = values.filter((v) => typeof v === "number" && !isNaN(v));
|
|
8
|
-
if (numericValues.length === 0) {
|
|
9
|
-
return () => ({});
|
|
10
|
-
}
|
|
11
|
-
const min = Math.min(...numericValues);
|
|
12
|
-
const max = Math.max(...numericValues);
|
|
13
|
-
return (x) => {
|
|
14
|
-
if (typeof x !== "number" || isNaN(x) || max === min) {
|
|
15
|
-
return {};
|
|
16
|
-
}
|
|
17
|
-
const nonRed = 255 - Math.round(255 * (x - min) / (max - min));
|
|
18
|
-
return { backgroundColor: `rgb(255,${nonRed},${nonRed})` };
|
|
19
|
-
};
|
|
20
|
-
}
|
|
21
|
-
function makeMultiValueRenderer(opts = {}) {
|
|
22
|
-
return vue.defineComponent({
|
|
23
|
-
name: opts.name || "vue3-multi-value-table",
|
|
24
|
-
props: {
|
|
25
|
-
// Data props
|
|
26
|
-
data: {
|
|
27
|
-
type: [Array, Object, Function],
|
|
28
|
-
required: true
|
|
29
|
-
},
|
|
30
|
-
rows: {
|
|
31
|
-
type: Array,
|
|
32
|
-
default: () => []
|
|
33
|
-
},
|
|
34
|
-
cols: {
|
|
35
|
-
type: Array,
|
|
36
|
-
default: () => []
|
|
37
|
-
},
|
|
38
|
-
vals: {
|
|
39
|
-
type: Array,
|
|
40
|
-
default: () => []
|
|
41
|
-
},
|
|
42
|
-
// Multi-value specific props
|
|
43
|
-
aggregatorMap: {
|
|
44
|
-
type: Object,
|
|
45
|
-
default: () => ({})
|
|
46
|
-
},
|
|
47
|
-
aggregators: {
|
|
48
|
-
type: Object,
|
|
49
|
-
required: true
|
|
50
|
-
},
|
|
51
|
-
// Filter and sort props
|
|
52
|
-
valueFilter: {
|
|
53
|
-
type: Object,
|
|
54
|
-
default: () => ({})
|
|
55
|
-
},
|
|
56
|
-
sorters: {
|
|
57
|
-
type: [Object, Function],
|
|
58
|
-
default: () => ({})
|
|
59
|
-
},
|
|
60
|
-
derivedAttributes: {
|
|
61
|
-
type: [Object, Function],
|
|
62
|
-
default: () => ({})
|
|
63
|
-
},
|
|
64
|
-
rowOrder: {
|
|
65
|
-
type: String,
|
|
66
|
-
default: "key_a_to_z"
|
|
67
|
-
},
|
|
68
|
-
colOrder: {
|
|
69
|
-
type: String,
|
|
70
|
-
default: "key_a_to_z"
|
|
71
|
-
},
|
|
72
|
-
// Display props
|
|
73
|
-
rowTotal: {
|
|
74
|
-
type: Boolean,
|
|
75
|
-
default: true
|
|
76
|
-
},
|
|
77
|
-
colTotal: {
|
|
78
|
-
type: Boolean,
|
|
79
|
-
default: true
|
|
80
|
-
},
|
|
81
|
-
tableColorScaleGenerator: {
|
|
82
|
-
type: Function,
|
|
83
|
-
default: () => redColorScaleGenerator
|
|
84
|
-
},
|
|
85
|
-
tableOptions: {
|
|
86
|
-
type: Object,
|
|
87
|
-
default: () => ({})
|
|
88
|
-
},
|
|
89
|
-
localeStrings: {
|
|
90
|
-
type: Object,
|
|
91
|
-
default: () => ({ totals: "Totals" })
|
|
92
|
-
},
|
|
93
|
-
labels: {
|
|
94
|
-
type: Object,
|
|
95
|
-
default: () => ({})
|
|
96
|
-
},
|
|
97
|
-
// Multi-value display options
|
|
98
|
-
cellLayout: {
|
|
99
|
-
type: String,
|
|
100
|
-
default: "vertical",
|
|
101
|
-
validator: (v) => ["vertical", "horizontal", "compact"].includes(v)
|
|
102
|
-
},
|
|
103
|
-
showValueLabels: {
|
|
104
|
-
type: Boolean,
|
|
105
|
-
default: true
|
|
106
|
-
},
|
|
107
|
-
valueLabels: {
|
|
108
|
-
type: Object,
|
|
109
|
-
default: () => ({})
|
|
110
|
-
}
|
|
111
|
-
},
|
|
112
|
-
setup(props) {
|
|
113
|
-
const applyLabel = (attr, value) => {
|
|
114
|
-
if (props.labels && typeof props.labels[attr] === "function") {
|
|
115
|
-
return props.labels[attr](value);
|
|
116
|
-
}
|
|
117
|
-
return value;
|
|
118
|
-
};
|
|
119
|
-
const getValueLabel = (valName) => {
|
|
120
|
-
if (props.valueLabels && props.valueLabels[valName]) {
|
|
121
|
-
return props.valueLabels[valName];
|
|
122
|
-
}
|
|
123
|
-
return valName;
|
|
124
|
-
};
|
|
125
|
-
const spanSize = (arr, i, j) => {
|
|
126
|
-
let x;
|
|
127
|
-
if (i !== 0) {
|
|
128
|
-
let noDraw = true;
|
|
129
|
-
for (x = 0; x <= j; x++) {
|
|
130
|
-
if (arr[i - 1][x] !== arr[i][x]) {
|
|
131
|
-
noDraw = false;
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
if (noDraw) return -1;
|
|
135
|
-
}
|
|
136
|
-
let len = 0;
|
|
137
|
-
while (i + len < arr.length) {
|
|
138
|
-
let stop = false;
|
|
139
|
-
for (x = 0; x <= j; x++) {
|
|
140
|
-
if (arr[i][x] !== arr[i + len][x]) {
|
|
141
|
-
stop = true;
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
if (stop) break;
|
|
145
|
-
len++;
|
|
146
|
-
}
|
|
147
|
-
return len;
|
|
148
|
-
};
|
|
149
|
-
const formatValue = (valName, value) => {
|
|
150
|
-
const aggName = props.aggregatorMap[valName] || "Sum";
|
|
151
|
-
const agg = props.aggregators[aggName];
|
|
152
|
-
if (agg) {
|
|
153
|
-
try {
|
|
154
|
-
const instance = agg([valName])();
|
|
155
|
-
if (instance && instance.format) {
|
|
156
|
-
return instance.format(value);
|
|
157
|
-
}
|
|
158
|
-
} catch (e) {
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
if (value === null || value === void 0) return "";
|
|
162
|
-
if (typeof value === "number") {
|
|
163
|
-
return value.toLocaleString();
|
|
164
|
-
}
|
|
165
|
-
return String(value);
|
|
166
|
-
};
|
|
167
|
-
const renderMultiValueCell = (values) => {
|
|
168
|
-
if (!values || typeof values !== "object") {
|
|
169
|
-
return String(values || "");
|
|
170
|
-
}
|
|
171
|
-
const items = props.vals.map((val) => {
|
|
172
|
-
const value = values[val];
|
|
173
|
-
const formatted = formatValue(val, value);
|
|
174
|
-
const label = getValueLabel(val);
|
|
175
|
-
const aggName = props.aggregatorMap[val] || "Sum";
|
|
176
|
-
if (props.cellLayout === "compact") {
|
|
177
|
-
return formatted;
|
|
178
|
-
}
|
|
179
|
-
return vue.h("div", {
|
|
180
|
-
class: "multi-value-item",
|
|
181
|
-
key: val,
|
|
182
|
-
"data-value": val,
|
|
183
|
-
"data-aggregator": aggName
|
|
184
|
-
}, [
|
|
185
|
-
props.showValueLabels ? vue.h("span", { class: "multi-value-label" }, `${label}: `) : null,
|
|
186
|
-
vue.h("span", { class: "multi-value-value" }, formatted)
|
|
187
|
-
]);
|
|
188
|
-
});
|
|
189
|
-
if (props.cellLayout === "compact") {
|
|
190
|
-
return items.join(" / ");
|
|
191
|
-
}
|
|
192
|
-
return vue.h("div", {
|
|
193
|
-
class: ["multi-value-cell", `layout-${props.cellLayout}`]
|
|
194
|
-
}, items);
|
|
195
|
-
};
|
|
196
|
-
const createPivotData = () => {
|
|
197
|
-
const multiValueAgg = MultiValuePivotData.createMultiValueAggregator(
|
|
198
|
-
props.aggregatorMap,
|
|
199
|
-
props.aggregators,
|
|
200
|
-
props.vals
|
|
201
|
-
);
|
|
202
|
-
const modifiedAggregators = {
|
|
203
|
-
...props.aggregators,
|
|
204
|
-
"Multi-Value": () => multiValueAgg
|
|
205
|
-
};
|
|
206
|
-
return new PivotData({
|
|
207
|
-
data: props.data,
|
|
208
|
-
rows: props.rows,
|
|
209
|
-
cols: props.cols,
|
|
210
|
-
vals: props.vals,
|
|
211
|
-
aggregators: modifiedAggregators,
|
|
212
|
-
aggregatorName: "Multi-Value",
|
|
213
|
-
valueFilter: props.valueFilter,
|
|
214
|
-
sorters: props.sorters,
|
|
215
|
-
derivedAttributes: props.derivedAttributes,
|
|
216
|
-
rowOrder: props.rowOrder,
|
|
217
|
-
colOrder: props.colOrder
|
|
218
|
-
});
|
|
219
|
-
};
|
|
220
|
-
return {
|
|
221
|
-
applyLabel,
|
|
222
|
-
getValueLabel,
|
|
223
|
-
spanSize,
|
|
224
|
-
formatValue,
|
|
225
|
-
renderMultiValueCell,
|
|
226
|
-
createPivotData
|
|
227
|
-
};
|
|
228
|
-
},
|
|
229
|
-
render() {
|
|
230
|
-
let pivotData;
|
|
231
|
-
try {
|
|
232
|
-
pivotData = this.createPivotData();
|
|
233
|
-
} catch (error) {
|
|
234
|
-
console.error("Multi-Value Renderer Error:", error);
|
|
235
|
-
return vue.h("div", { class: "pvtError" }, `Error: ${error.message}`);
|
|
236
|
-
}
|
|
237
|
-
const colAttrs = pivotData.props.cols;
|
|
238
|
-
const rowAttrs = pivotData.props.rows;
|
|
239
|
-
const rowKeys = pivotData.getRowKeys();
|
|
240
|
-
const colKeys = pivotData.getColKeys();
|
|
241
|
-
const grandTotalAggregator = pivotData.getAggregator([], []);
|
|
242
|
-
const getClickHandler = (value, rowValues, colValues) => {
|
|
243
|
-
var _a;
|
|
244
|
-
if ((_a = this.tableOptions) == null ? void 0 : _a.clickCallback) {
|
|
245
|
-
const filters = {};
|
|
246
|
-
colAttrs.forEach((attr, i) => {
|
|
247
|
-
if (colValues[i] !== null) {
|
|
248
|
-
filters[attr] = colValues[i];
|
|
249
|
-
}
|
|
250
|
-
});
|
|
251
|
-
rowAttrs.forEach((attr, i) => {
|
|
252
|
-
if (rowValues[i] !== null) {
|
|
253
|
-
filters[attr] = rowValues[i];
|
|
254
|
-
}
|
|
255
|
-
});
|
|
256
|
-
return (e) => this.tableOptions.clickCallback(e, value, filters, pivotData);
|
|
257
|
-
}
|
|
258
|
-
return null;
|
|
259
|
-
};
|
|
260
|
-
return vue.h("table", { class: ["pvtTable", "pvtMultiValueTable"] }, [
|
|
261
|
-
// THEAD
|
|
262
|
-
vue.h("thead", [
|
|
263
|
-
// Column attribute headers
|
|
264
|
-
...colAttrs.map((c, j) => {
|
|
265
|
-
return vue.h("tr", { key: `colAttrs${j}` }, [
|
|
266
|
-
// Top-left corner cell
|
|
267
|
-
j === 0 && rowAttrs.length !== 0 ? vue.h("th", { colSpan: rowAttrs.length, rowSpan: colAttrs.length }) : void 0,
|
|
268
|
-
// Column attribute label
|
|
269
|
-
vue.h("th", { class: "pvtAxisLabel" }, c),
|
|
270
|
-
// Column keys
|
|
271
|
-
...colKeys.map((colKey, i) => {
|
|
272
|
-
const x = this.spanSize(colKeys, i, j);
|
|
273
|
-
if (x === -1) return null;
|
|
274
|
-
return vue.h("th", {
|
|
275
|
-
class: "pvtColLabel",
|
|
276
|
-
key: `colKey${i}`,
|
|
277
|
-
colSpan: x,
|
|
278
|
-
rowSpan: j === colAttrs.length - 1 && rowAttrs.length !== 0 ? 2 : 1
|
|
279
|
-
}, this.applyLabel(colAttrs[j], colKey[j]));
|
|
280
|
-
}),
|
|
281
|
-
// Totals header
|
|
282
|
-
j === 0 && this.rowTotal ? vue.h("th", {
|
|
283
|
-
class: "pvtTotalLabel",
|
|
284
|
-
rowSpan: colAttrs.length + (rowAttrs.length === 0 ? 0 : 1)
|
|
285
|
-
}, this.localeStrings.totals) : void 0
|
|
286
|
-
].filter(Boolean));
|
|
287
|
-
}),
|
|
288
|
-
// Row attribute labels row
|
|
289
|
-
rowAttrs.length !== 0 ? vue.h("tr", [
|
|
290
|
-
...rowAttrs.map((r, i) => {
|
|
291
|
-
return vue.h("th", { class: "pvtAxisLabel", key: `rowAttr${i}` }, r);
|
|
292
|
-
}),
|
|
293
|
-
this.rowTotal ? vue.h(
|
|
294
|
-
"th",
|
|
295
|
-
{ class: "pvtTotalLabel" },
|
|
296
|
-
colAttrs.length === 0 ? this.localeStrings.totals : null
|
|
297
|
-
) : colAttrs.length === 0 ? void 0 : vue.h("th")
|
|
298
|
-
].filter(Boolean)) : void 0
|
|
299
|
-
].filter(Boolean)),
|
|
300
|
-
// TBODY
|
|
301
|
-
vue.h("tbody", [
|
|
302
|
-
// Data rows
|
|
303
|
-
...rowKeys.map((rowKey, i) => {
|
|
304
|
-
const totalAggregator = pivotData.getAggregator(rowKey, []);
|
|
305
|
-
return vue.h("tr", { key: `rowKeyRow${i}` }, [
|
|
306
|
-
// Row labels
|
|
307
|
-
...rowKey.map((text, j) => {
|
|
308
|
-
const x = this.spanSize(rowKeys, i, j);
|
|
309
|
-
if (x === -1) return null;
|
|
310
|
-
return vue.h("th", {
|
|
311
|
-
class: "pvtRowLabel",
|
|
312
|
-
key: `rowKeyLabel${i}-${j}`,
|
|
313
|
-
rowSpan: x,
|
|
314
|
-
colSpan: j === rowAttrs.length - 1 && colAttrs.length !== 0 ? 2 : 1
|
|
315
|
-
}, this.applyLabel(rowAttrs[j], text));
|
|
316
|
-
}),
|
|
317
|
-
// Data cells
|
|
318
|
-
...colKeys.map((colKey, j) => {
|
|
319
|
-
const aggregator = pivotData.getAggregator(rowKey, colKey);
|
|
320
|
-
const value = aggregator.value();
|
|
321
|
-
const clickHandler = getClickHandler(value, rowKey, colKey);
|
|
322
|
-
return vue.h("td", {
|
|
323
|
-
class: ["pvVal", "pvtMultiVal"],
|
|
324
|
-
key: `pvtVal${i}-${j}`,
|
|
325
|
-
onClick: clickHandler
|
|
326
|
-
}, [this.renderMultiValueCell(value)]);
|
|
327
|
-
}),
|
|
328
|
-
// Row total
|
|
329
|
-
this.rowTotal ? vue.h("td", {
|
|
330
|
-
class: ["pvtTotal", "pvtMultiVal"],
|
|
331
|
-
onClick: getClickHandler(totalAggregator.value(), rowKey, [])
|
|
332
|
-
}, [this.renderMultiValueCell(totalAggregator.value())]) : void 0
|
|
333
|
-
].filter(Boolean));
|
|
334
|
-
}),
|
|
335
|
-
// Column totals row
|
|
336
|
-
vue.h("tr", [
|
|
337
|
-
this.colTotal ? vue.h("th", {
|
|
338
|
-
class: "pvtTotalLabel",
|
|
339
|
-
colSpan: rowAttrs.length + (colAttrs.length === 0 ? 0 : 1)
|
|
340
|
-
}, this.localeStrings.totals) : void 0,
|
|
341
|
-
...this.colTotal ? colKeys.map((colKey, i) => {
|
|
342
|
-
const totalAggregator = pivotData.getAggregator([], colKey);
|
|
343
|
-
const clickHandler = getClickHandler(totalAggregator.value(), [], colKey);
|
|
344
|
-
return vue.h("td", {
|
|
345
|
-
class: ["pvtTotal", "pvtMultiVal"],
|
|
346
|
-
key: `total${i}`,
|
|
347
|
-
onClick: clickHandler
|
|
348
|
-
}, [this.renderMultiValueCell(totalAggregator.value())]);
|
|
349
|
-
}) : [],
|
|
350
|
-
this.colTotal && this.rowTotal ? vue.h("td", {
|
|
351
|
-
class: ["pvtGrandTotal", "pvtMultiVal"],
|
|
352
|
-
onClick: getClickHandler(grandTotalAggregator.value(), [], [])
|
|
353
|
-
}, [this.renderMultiValueCell(grandTotalAggregator.value())]) : void 0
|
|
354
|
-
].filter(Boolean))
|
|
355
|
-
])
|
|
356
|
-
]);
|
|
357
|
-
}
|
|
358
|
-
});
|
|
359
|
-
}
|
|
360
|
-
const MultiValueTableRenderer = vue.markRaw({
|
|
361
|
-
"Multi-Value Table": makeMultiValueRenderer({ name: "vue3-multi-value-table" })
|
|
362
|
-
});
|
|
363
|
-
exports.MultiValueTableRenderer = MultiValueTableRenderer;
|
|
364
|
-
exports.makeMultiValueRenderer = makeMultiValueRenderer;
|