@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/dist/vue2.js ADDED
@@ -0,0 +1,399 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
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 {
23
+ name: opts.name || "vue2-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
+ methods: {
113
+ /**
114
+ * Apply label transformation to a value
115
+ */
116
+ applyLabel(attr, value) {
117
+ if (this.labels && typeof this.labels[attr] === "function") {
118
+ return this.labels[attr](value);
119
+ }
120
+ return value;
121
+ },
122
+ /**
123
+ * Get display label for a value column
124
+ */
125
+ getValueLabel(valName) {
126
+ if (this.valueLabels && this.valueLabels[valName]) {
127
+ return this.valueLabels[valName];
128
+ }
129
+ return valName;
130
+ },
131
+ /**
132
+ * Calculate row/col span for merged cells
133
+ */
134
+ spanSize(arr, i, j) {
135
+ let x;
136
+ if (i !== 0) {
137
+ let noDraw = true;
138
+ for (x = 0; x <= j; x++) {
139
+ if (arr[i - 1][x] !== arr[i][x]) {
140
+ noDraw = false;
141
+ }
142
+ }
143
+ if (noDraw) return -1;
144
+ }
145
+ let len = 0;
146
+ while (i + len < arr.length) {
147
+ let stop = false;
148
+ for (x = 0; x <= j; x++) {
149
+ if (arr[i][x] !== arr[i + len][x]) {
150
+ stop = true;
151
+ }
152
+ }
153
+ if (stop) break;
154
+ len++;
155
+ }
156
+ return len;
157
+ },
158
+ /**
159
+ * Format a single value using the appropriate aggregator
160
+ */
161
+ formatValue(valName, value) {
162
+ const aggName = this.aggregatorMap[valName] || "Sum";
163
+ const agg = this.aggregators[aggName];
164
+ if (agg) {
165
+ try {
166
+ const instance = agg([valName])();
167
+ if (instance && instance.format) {
168
+ return instance.format(value);
169
+ }
170
+ } catch (e) {
171
+ }
172
+ }
173
+ if (value === null || value === void 0) return "";
174
+ if (typeof value === "number") {
175
+ return value.toLocaleString();
176
+ }
177
+ return String(value);
178
+ },
179
+ /**
180
+ * Render multi-value cell content
181
+ */
182
+ renderMultiValueCell(h, values, rowKey, colKey) {
183
+ if (!values || typeof values !== "object") {
184
+ return String(values || "");
185
+ }
186
+ const items = this.vals.map((val) => {
187
+ const value = values[val];
188
+ const formatted = this.formatValue(val, value);
189
+ const label = this.getValueLabel(val);
190
+ const aggName = this.aggregatorMap[val] || "Sum";
191
+ if (this.cellLayout === "compact") {
192
+ return formatted;
193
+ }
194
+ return h("div", {
195
+ staticClass: "multi-value-item",
196
+ key: val,
197
+ attrs: {
198
+ "data-value": val,
199
+ "data-aggregator": aggName
200
+ }
201
+ }, [
202
+ this.showValueLabels ? h("span", {
203
+ staticClass: "multi-value-label"
204
+ }, `${label}: `) : null,
205
+ h("span", {
206
+ staticClass: ["multi-value-value"]
207
+ }, formatted)
208
+ ]);
209
+ });
210
+ if (this.cellLayout === "compact") {
211
+ return items.join(" / ");
212
+ }
213
+ return h("div", {
214
+ staticClass: ["multi-value-cell", `layout-${this.cellLayout}`]
215
+ }, items);
216
+ },
217
+ /**
218
+ * Create PivotData-like structure with multi-value aggregation
219
+ */
220
+ createPivotData() {
221
+ const multiValueAgg = MultiValuePivotData.createMultiValueAggregator(
222
+ this.aggregatorMap,
223
+ this.aggregators,
224
+ this.vals
225
+ );
226
+ const modifiedAggregators = {
227
+ ...this.aggregators,
228
+ "Multi-Value": () => multiValueAgg
229
+ };
230
+ return new PivotData({
231
+ data: this.data,
232
+ rows: this.rows,
233
+ cols: this.cols,
234
+ vals: this.vals,
235
+ aggregators: modifiedAggregators,
236
+ aggregatorName: "Multi-Value",
237
+ valueFilter: this.valueFilter,
238
+ sorters: this.sorters,
239
+ derivedAttributes: this.derivedAttributes,
240
+ rowOrder: this.rowOrder,
241
+ colOrder: this.colOrder
242
+ });
243
+ }
244
+ },
245
+ render(h) {
246
+ let pivotData;
247
+ try {
248
+ pivotData = this.createPivotData();
249
+ } catch (error) {
250
+ console.error("Multi-Value Renderer Error:", error);
251
+ return h("div", {
252
+ staticClass: ["pvtError"]
253
+ }, `Error: ${error.message}`);
254
+ }
255
+ const colAttrs = pivotData.props.cols;
256
+ const rowAttrs = pivotData.props.rows;
257
+ const rowKeys = pivotData.getRowKeys();
258
+ const colKeys = pivotData.getColKeys();
259
+ const grandTotalAggregator = pivotData.getAggregator([], []);
260
+ const getClickHandler = (value, rowValues, colValues) => {
261
+ if (this.tableOptions && this.tableOptions.clickCallback) {
262
+ const filters = {};
263
+ colAttrs.forEach((attr, i) => {
264
+ if (colValues[i] !== null) {
265
+ filters[attr] = colValues[i];
266
+ }
267
+ });
268
+ rowAttrs.forEach((attr, i) => {
269
+ if (rowValues[i] !== null) {
270
+ filters[attr] = rowValues[i];
271
+ }
272
+ });
273
+ return (e) => this.tableOptions.clickCallback(e, value, filters, pivotData);
274
+ }
275
+ return null;
276
+ };
277
+ return h("table", {
278
+ staticClass: ["pvtTable", "pvtMultiValueTable"]
279
+ }, [
280
+ // THEAD
281
+ h("thead", [
282
+ // Column attribute headers
283
+ colAttrs.map((c, j) => {
284
+ return h("tr", { key: `colAttrs${j}` }, [
285
+ // Top-left corner cell
286
+ j === 0 && rowAttrs.length !== 0 ? h("th", {
287
+ attrs: {
288
+ colSpan: rowAttrs.length,
289
+ rowSpan: colAttrs.length
290
+ }
291
+ }) : void 0,
292
+ // Column attribute label
293
+ h("th", { staticClass: ["pvtAxisLabel"] }, c),
294
+ // Column keys
295
+ colKeys.map((colKey, i) => {
296
+ const x = this.spanSize(colKeys, i, j);
297
+ if (x === -1) return null;
298
+ return h("th", {
299
+ staticClass: ["pvtColLabel"],
300
+ attrs: {
301
+ key: `colKey${i}`,
302
+ colSpan: x,
303
+ rowSpan: j === colAttrs.length - 1 && rowAttrs.length !== 0 ? 2 : 1
304
+ }
305
+ }, this.applyLabel(colAttrs[j], colKey[j]));
306
+ }),
307
+ // Totals header
308
+ j === 0 && this.rowTotal ? h("th", {
309
+ staticClass: ["pvtTotalLabel"],
310
+ attrs: {
311
+ rowSpan: colAttrs.length + (rowAttrs.length === 0 ? 0 : 1)
312
+ }
313
+ }, this.localeStrings.totals) : void 0
314
+ ]);
315
+ }),
316
+ // Row attribute labels row
317
+ rowAttrs.length !== 0 ? h("tr", [
318
+ rowAttrs.map((r, i) => {
319
+ return h("th", {
320
+ staticClass: ["pvtAxisLabel"],
321
+ key: `rowAttr${i}`
322
+ }, r);
323
+ }),
324
+ this.rowTotal ? h(
325
+ "th",
326
+ { staticClass: ["pvtTotalLabel"] },
327
+ colAttrs.length === 0 ? this.localeStrings.totals : null
328
+ ) : colAttrs.length === 0 ? void 0 : h("th")
329
+ ]) : void 0
330
+ ]),
331
+ // TBODY
332
+ h("tbody", [
333
+ // Data rows
334
+ rowKeys.map((rowKey, i) => {
335
+ const totalAggregator = pivotData.getAggregator(rowKey, []);
336
+ return h("tr", { key: `rowKeyRow${i}` }, [
337
+ // Row labels
338
+ rowKey.map((text, j) => {
339
+ const x = this.spanSize(rowKeys, i, j);
340
+ if (x === -1) return null;
341
+ return h("th", {
342
+ staticClass: ["pvtRowLabel"],
343
+ attrs: {
344
+ key: `rowKeyLabel${i}-${j}`,
345
+ rowSpan: x,
346
+ colSpan: j === rowAttrs.length - 1 && colAttrs.length !== 0 ? 2 : 1
347
+ }
348
+ }, this.applyLabel(rowAttrs[j], text));
349
+ }),
350
+ // Data cells
351
+ colKeys.map((colKey, j) => {
352
+ const aggregator = pivotData.getAggregator(rowKey, colKey);
353
+ const value = aggregator.value();
354
+ const clickHandler = getClickHandler(value, rowKey, colKey);
355
+ return h("td", {
356
+ staticClass: ["pvVal", "pvtMultiVal"],
357
+ attrs: { key: `pvtVal${i}-${j}` },
358
+ on: clickHandler ? { click: clickHandler } : {}
359
+ }, [this.renderMultiValueCell(h, value, rowKey, colKey)]);
360
+ }),
361
+ // Row total
362
+ this.rowTotal ? h("td", {
363
+ staticClass: ["pvtTotal", "pvtMultiVal"],
364
+ on: getClickHandler(totalAggregator.value(), rowKey, []) ? { click: getClickHandler(totalAggregator.value(), rowKey, []) } : {}
365
+ }, [this.renderMultiValueCell(h, totalAggregator.value(), rowKey, [])]) : void 0
366
+ ]);
367
+ }),
368
+ // Column totals row
369
+ h("tr", [
370
+ this.colTotal ? h("th", {
371
+ staticClass: ["pvtTotalLabel"],
372
+ attrs: {
373
+ colSpan: rowAttrs.length + (colAttrs.length === 0 ? 0 : 1)
374
+ }
375
+ }, this.localeStrings.totals) : void 0,
376
+ this.colTotal ? colKeys.map((colKey, i) => {
377
+ const totalAggregator = pivotData.getAggregator([], colKey);
378
+ const clickHandler = getClickHandler(totalAggregator.value(), [], colKey);
379
+ return h("td", {
380
+ staticClass: ["pvtTotal", "pvtMultiVal"],
381
+ attrs: { key: `total${i}` },
382
+ on: clickHandler ? { click: clickHandler } : {}
383
+ }, [this.renderMultiValueCell(h, totalAggregator.value(), [], colKey)]);
384
+ }) : void 0,
385
+ this.colTotal && this.rowTotal ? h("td", {
386
+ staticClass: ["pvtGrandTotal", "pvtMultiVal"],
387
+ on: getClickHandler(grandTotalAggregator.value(), [], []) ? { click: getClickHandler(grandTotalAggregator.value(), [], []) } : {}
388
+ }, [this.renderMultiValueCell(h, grandTotalAggregator.value(), [], [])]) : void 0
389
+ ])
390
+ ])
391
+ ]);
392
+ }
393
+ };
394
+ }
395
+ const MultiValueTableRenderer = {
396
+ "Multi-Value Table": makeMultiValueRenderer({ name: "vue2-multi-value-table" })
397
+ };
398
+ exports.MultiValueRenderers = MultiValueTableRenderer;
399
+ exports.makeMultiValueRenderer = makeMultiValueRenderer;