@vue-pivottable/multi-value-renderer 0.2.2 → 0.3.0-beta.1

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