@vue-pivottable/multi-value-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/LICENSE +21 -0
- package/README.md +167 -0
- package/dist/MultiValuePivotData.js +81 -0
- package/dist/MultiValueTableRenderer.js +364 -0
- package/dist/core.js +92 -0
- package/dist/core.mjs +92 -0
- package/dist/index.js +12 -0
- package/dist/index.mjs +12 -0
- package/dist/styles.css +119 -0
- package/dist/vue2.js +399 -0
- package/dist/vue2.mjs +399 -0
- package/dist/vue3.js +5 -0
- package/dist/vue3.mjs +5 -0
- package/package.json +81 -0
- package/types/index.d.ts +170 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Seungwoo321
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
# @vue-pivottable/multi-value-renderer
|
|
2
|
+
|
|
3
|
+
Multi-value aggregator renderer for vue-pivottable. Display multiple aggregated values per cell, each with its own aggregation function.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Multiple Values per Cell**: Display sales (Sum), quantity (Average), and more in a single pivot table cell
|
|
8
|
+
- **Vue 2 & Vue 3 Support**: Works with both vue-pivottable (Vue 2) and vue3-pivottable (Vue 3)
|
|
9
|
+
- **Flexible Layout**: Choose between vertical, horizontal, or compact cell layouts
|
|
10
|
+
- **Custom Value Labels**: Display user-friendly labels for each value column
|
|
11
|
+
- **Full Integration**: Works with existing vue-pivottable features like click callbacks, labels, and totals
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install @vue-pivottable/multi-value-renderer
|
|
17
|
+
|
|
18
|
+
# or
|
|
19
|
+
pnpm add @vue-pivottable/multi-value-renderer
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Usage
|
|
23
|
+
|
|
24
|
+
### Vue 3
|
|
25
|
+
|
|
26
|
+
```vue
|
|
27
|
+
<template>
|
|
28
|
+
<VuePivottable
|
|
29
|
+
:data="data"
|
|
30
|
+
:rows="['region']"
|
|
31
|
+
:cols="['product']"
|
|
32
|
+
:vals="['sales', 'quantity']"
|
|
33
|
+
:renderers="renderers"
|
|
34
|
+
renderer-name="Multi-Value Table"
|
|
35
|
+
:aggregator-map="aggregatorMap"
|
|
36
|
+
/>
|
|
37
|
+
</template>
|
|
38
|
+
|
|
39
|
+
<script setup>
|
|
40
|
+
import { VuePivottable, PivotUtilities } from 'vue-pivottable'
|
|
41
|
+
import { MultiValueRenderers } from '@vue-pivottable/multi-value-renderer'
|
|
42
|
+
|
|
43
|
+
const data = [
|
|
44
|
+
{ region: 'East', product: 'Apple', sales: 100, quantity: 10 },
|
|
45
|
+
{ region: 'East', product: 'Banana', sales: 80, quantity: 20 },
|
|
46
|
+
// ...
|
|
47
|
+
]
|
|
48
|
+
|
|
49
|
+
const renderers = {
|
|
50
|
+
...MultiValueRenderers
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Different aggregation for each value
|
|
54
|
+
const aggregatorMap = {
|
|
55
|
+
sales: 'Sum',
|
|
56
|
+
quantity: 'Average'
|
|
57
|
+
}
|
|
58
|
+
</script>
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Vue 2
|
|
62
|
+
|
|
63
|
+
```vue
|
|
64
|
+
<template>
|
|
65
|
+
<VuePivottable
|
|
66
|
+
:data="data"
|
|
67
|
+
:rows="['region']"
|
|
68
|
+
:cols="['product']"
|
|
69
|
+
:vals="['sales', 'quantity']"
|
|
70
|
+
:renderers="renderers"
|
|
71
|
+
renderer-name="Multi-Value Table"
|
|
72
|
+
:aggregator-map="aggregatorMap"
|
|
73
|
+
/>
|
|
74
|
+
</template>
|
|
75
|
+
|
|
76
|
+
<script>
|
|
77
|
+
import { VuePivottable } from 'vue-pivottable'
|
|
78
|
+
import { MultiValueRenderers } from '@vue-pivottable/multi-value-renderer/vue2'
|
|
79
|
+
|
|
80
|
+
export default {
|
|
81
|
+
components: { VuePivottable },
|
|
82
|
+
data() {
|
|
83
|
+
return {
|
|
84
|
+
data: [
|
|
85
|
+
{ region: 'East', product: 'Apple', sales: 100, quantity: 10 },
|
|
86
|
+
// ...
|
|
87
|
+
],
|
|
88
|
+
renderers: { ...MultiValueRenderers },
|
|
89
|
+
aggregatorMap: {
|
|
90
|
+
sales: 'Sum',
|
|
91
|
+
quantity: 'Average'
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
</script>
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## Props
|
|
100
|
+
|
|
101
|
+
### aggregatorMap
|
|
102
|
+
|
|
103
|
+
Map of value column names to aggregator names.
|
|
104
|
+
|
|
105
|
+
```js
|
|
106
|
+
{
|
|
107
|
+
sales: 'Sum',
|
|
108
|
+
quantity: 'Average',
|
|
109
|
+
profit: 'Maximum'
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### cellLayout
|
|
114
|
+
|
|
115
|
+
How to display multiple values in each cell:
|
|
116
|
+
|
|
117
|
+
- `'vertical'` (default): Stack values vertically
|
|
118
|
+
- `'horizontal'`: Display values side by side
|
|
119
|
+
- `'compact'`: Show values separated by " / "
|
|
120
|
+
|
|
121
|
+
### showValueLabels
|
|
122
|
+
|
|
123
|
+
Whether to show labels before each value (default: `true`).
|
|
124
|
+
|
|
125
|
+
### valueLabels
|
|
126
|
+
|
|
127
|
+
Custom display labels for value columns:
|
|
128
|
+
|
|
129
|
+
```js
|
|
130
|
+
{
|
|
131
|
+
sales: 'Total Sales',
|
|
132
|
+
quantity: 'Avg Qty'
|
|
133
|
+
}
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
## Styling
|
|
137
|
+
|
|
138
|
+
Import the included CSS for default styling:
|
|
139
|
+
|
|
140
|
+
```js
|
|
141
|
+
import '@vue-pivottable/multi-value-renderer/dist/styles.css'
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Or customize with your own CSS targeting these classes:
|
|
145
|
+
|
|
146
|
+
- `.multi-value-cell` - Cell container
|
|
147
|
+
- `.multi-value-item` - Individual value row
|
|
148
|
+
- `.multi-value-label` - Value label (e.g., "Sales:")
|
|
149
|
+
- `.multi-value-value` - The actual value
|
|
150
|
+
- `.layout-vertical`, `.layout-horizontal` - Layout modifiers
|
|
151
|
+
|
|
152
|
+
## Available Aggregators
|
|
153
|
+
|
|
154
|
+
The renderer uses aggregators from vue-pivottable. Common options:
|
|
155
|
+
|
|
156
|
+
- `Count`
|
|
157
|
+
- `Sum`
|
|
158
|
+
- `Average`
|
|
159
|
+
- `Minimum`
|
|
160
|
+
- `Maximum`
|
|
161
|
+
- `Median`
|
|
162
|
+
- `Sample Variance`
|
|
163
|
+
- `Sample Standard Deviation`
|
|
164
|
+
|
|
165
|
+
## License
|
|
166
|
+
|
|
167
|
+
MIT
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
function createMultiValueAggregator(aggregatorMap, aggregators, vals) {
|
|
3
|
+
return function multiValueAggregatorFactory(data, rowKey, colKey) {
|
|
4
|
+
const subAggregators = {};
|
|
5
|
+
vals.forEach((val) => {
|
|
6
|
+
const aggName = aggregatorMap[val] || "Sum";
|
|
7
|
+
const aggFactory = aggregators[aggName];
|
|
8
|
+
if (aggFactory) {
|
|
9
|
+
subAggregators[val] = aggFactory([val])(data, rowKey, colKey);
|
|
10
|
+
}
|
|
11
|
+
});
|
|
12
|
+
return {
|
|
13
|
+
push(record) {
|
|
14
|
+
vals.forEach((val) => {
|
|
15
|
+
if (subAggregators[val]) {
|
|
16
|
+
subAggregators[val].push(record);
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
},
|
|
20
|
+
value() {
|
|
21
|
+
const results = {};
|
|
22
|
+
vals.forEach((val) => {
|
|
23
|
+
if (subAggregators[val]) {
|
|
24
|
+
results[val] = subAggregators[val].value();
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
return results;
|
|
28
|
+
},
|
|
29
|
+
valueOf(valName) {
|
|
30
|
+
if (subAggregators[valName]) {
|
|
31
|
+
return subAggregators[valName].value();
|
|
32
|
+
}
|
|
33
|
+
return null;
|
|
34
|
+
},
|
|
35
|
+
format(values) {
|
|
36
|
+
if (typeof values !== "object") {
|
|
37
|
+
return String(values ?? "");
|
|
38
|
+
}
|
|
39
|
+
return vals.map((val) => {
|
|
40
|
+
const v = values[val];
|
|
41
|
+
const subAgg = subAggregators[val];
|
|
42
|
+
return subAgg && subAgg.format ? subAgg.format(v) : String(v ?? "");
|
|
43
|
+
}).join(" / ");
|
|
44
|
+
},
|
|
45
|
+
formatOf(valName, value) {
|
|
46
|
+
if (subAggregators[valName] && subAggregators[valName].format) {
|
|
47
|
+
return subAggregators[valName].format(value);
|
|
48
|
+
}
|
|
49
|
+
return String(value ?? "");
|
|
50
|
+
},
|
|
51
|
+
getSubAggregator(valName) {
|
|
52
|
+
return subAggregators[valName] || null;
|
|
53
|
+
},
|
|
54
|
+
numInputs: vals.length
|
|
55
|
+
};
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
function extendPivotData(BasePivotData) {
|
|
59
|
+
return class MultiValuePivotData extends BasePivotData {
|
|
60
|
+
constructor(inputProps = {}) {
|
|
61
|
+
const { aggregatorMap = {}, ...restProps } = inputProps;
|
|
62
|
+
const multiValueAgg = createMultiValueAggregator(
|
|
63
|
+
aggregatorMap,
|
|
64
|
+
restProps.aggregators || BasePivotData.defaultProps.aggregators,
|
|
65
|
+
restProps.vals || []
|
|
66
|
+
);
|
|
67
|
+
const modifiedProps = {
|
|
68
|
+
...restProps,
|
|
69
|
+
aggregators: {
|
|
70
|
+
...restProps.aggregators,
|
|
71
|
+
"Multi-Value": () => multiValueAgg
|
|
72
|
+
},
|
|
73
|
+
aggregatorName: "Multi-Value"
|
|
74
|
+
};
|
|
75
|
+
super(modifiedProps);
|
|
76
|
+
this.aggregatorMap = aggregatorMap;
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
exports.createMultiValueAggregator = createMultiValueAggregator;
|
|
81
|
+
exports.extendPivotData = extendPivotData;
|
|
@@ -0,0 +1,364 @@
|
|
|
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;
|