@malloydata/render 0.0.60-dev230718174455 → 0.0.60

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.
@@ -1,4 +1,4 @@
1
- import { DataColumn, Field } from '@malloydata/malloy';
1
+ import { DataColumn, Field, SortableField } from '@malloydata/malloy';
2
2
  import { StyleDefaults } from '../data_styles';
3
3
  import { ContainerRenderer } from './container';
4
4
  import { Renderer } from '../renderer';
@@ -6,12 +6,13 @@ export declare class HTMLTableRenderer extends ContainerRenderer {
6
6
  protected childrenStyleDefaults: StyleDefaults;
7
7
  render(table: DataColumn): Promise<HTMLElement>;
8
8
  calculatePivotDimensions(table: DataColumn): {
9
- dimensions: Field[];
10
- nonDimensions: Field[];
9
+ dimensions: SortableField[];
10
+ nonDimensions: SortableField[];
11
11
  };
12
12
  generatePivotedCells(table: DataColumn, shouldTranspose: boolean): Promise<Map<string, Map<string, HTMLTableCellElement>>>;
13
13
  generateNoValueCell(field: Field, shouldTranspose: boolean): HTMLTableCellElement;
14
14
  createCellAndRender(childRenderer: Renderer, value: DataColumn, shouldTranspose: boolean): Promise<HTMLTableCellElement>;
15
+ createEmptyHeaderCell(): HTMLTableCellElement;
15
16
  createHeaderCell(field: Field, shouldTranspose: boolean): HTMLTableCellElement;
16
17
  createCell(childRenderer: Renderer, shouldTranspose: boolean): HTMLTableCellElement;
17
18
  renderChild(value: DataColumn): Promise<HTMLElement>;
@@ -29,13 +29,18 @@ const number_1 = require("./number");
29
29
  const utils_1 = require("./utils");
30
30
  const tags_utils_1 = require("../tags_utils");
31
31
  class PivotedField {
32
- constructor(parentField, values) {
32
+ constructor(parentField, values, span) {
33
33
  this.parentField = parentField;
34
34
  this.values = values;
35
+ this.span = span;
35
36
  this.key = JSON.stringify({
36
37
  parentField: this.parentField.name,
37
- values: this.values.filter(v => (v.isScalar() ? v.key : '')),
38
+ values: this.values.map(v => (v.isScalar() ? v.key : '')),
38
39
  });
40
+ this.fieldValueMap = new Map();
41
+ for (const value of this.values) {
42
+ this.fieldValueMap.set(value.field.name, value);
43
+ }
39
44
  }
40
45
  }
41
46
  class PivotedColumnField {
@@ -56,6 +61,9 @@ class HTMLTableRenderer extends container_1.ContainerRenderer {
56
61
  }
57
62
  async render(table) {
58
63
  var _a;
64
+ if (table.isNull()) {
65
+ return this.document.createElement('span');
66
+ }
59
67
  if (!table.isArray() && !table.isRecord()) {
60
68
  throw new Error('Invalid type for Table Renderer');
61
69
  }
@@ -94,7 +102,7 @@ class HTMLTableRenderer extends container_1.ContainerRenderer {
94
102
  nonDimensions = dimensionsResult.nonDimensions;
95
103
  }
96
104
  for (const innerRow of dc) {
97
- const pivotedField = new PivotedField(field, dimensions.map(d => innerRow.cell(d)));
105
+ const pivotedField = new PivotedField(field, dimensions.map(d => innerRow.cell(d.field)), nonDimensions.length);
98
106
  const pfKey = pivotedField.key;
99
107
  if (!pivotedFields[pfKey]) {
100
108
  pivotedFields.set(pfKey, pivotedField);
@@ -104,10 +112,26 @@ class HTMLTableRenderer extends container_1.ContainerRenderer {
104
112
  if (!dimensions) {
105
113
  throw new Error(`Could not pivot ${field.name}, no data found.`);
106
114
  }
107
- for (const pf of pivotedFields) {
115
+ const sortedPivotedFields = Array.from(pivotedFields.values()).sort((a, b) => {
116
+ for (const d of dimensions) {
117
+ const aValue = a.fieldValueMap.get(d.field.name);
118
+ const bValue = b.fieldValueMap.get(d.field.name);
119
+ if ((aValue === null || aValue === void 0 ? void 0 : aValue.isScalar()) &&
120
+ (bValue === null || bValue === void 0 ? void 0 : bValue.isScalar()) &&
121
+ typeof aValue === typeof bValue) {
122
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
123
+ const compValue = aValue.compareTo(bValue);
124
+ if (compValue !== 0) {
125
+ return d.dir !== 'desc' ? compValue : -compValue;
126
+ }
127
+ }
128
+ }
129
+ return 0;
130
+ });
131
+ for (const pf of sortedPivotedFields) {
108
132
  for (const nonDimension of nonDimensions) {
109
- columnFields.push(new PivotedColumnField(pf[1], nonDimension));
110
- cells[rowIndex][columnIndex] = childRenderer.createHeaderCell(nonDimension, shouldTranspose);
133
+ columnFields.push(new PivotedColumnField(pf, nonDimension.field));
134
+ cells[rowIndex][columnIndex] = childRenderer.createHeaderCell(nonDimension.field, shouldTranspose);
111
135
  columnIndex++;
112
136
  }
113
137
  }
@@ -140,29 +164,37 @@ class HTMLTableRenderer extends container_1.ContainerRenderer {
140
164
  let lastPivotedColumnHash = null;
141
165
  for (const field of columnFields) {
142
166
  if (field instanceof PivotedColumnField) {
143
- const headerCell = this.document.createElement('th');
144
- headerCell.style.cssText = `
145
- padding: 8px;
146
- color: var(--malloy-title-color, #505050);
147
- border-bottom: 1px solid var(--malloy-border-color, #eaeaea);
148
- `;
149
167
  const pfKey = field.pivotedField.key;
150
168
  const valueIndex = field.pivotedField.values.length - pivotDepth + r;
151
- if (lastPivotedColumnHash !== pfKey && valueIndex >= 0) {
169
+ if (valueIndex < 0) {
170
+ row.push(this.createEmptyHeaderCell());
171
+ }
172
+ else if (lastPivotedColumnHash !== pfKey) {
173
+ const headerCell = this.document.createElement('th');
174
+ if (!shouldTranspose) {
175
+ headerCell.colSpan = field.pivotedField.span;
176
+ }
177
+ else {
178
+ headerCell.rowSpan = field.pivotedField.span;
179
+ }
180
+ headerCell.style.cssText = `
181
+ padding: 8px;
182
+ color: var(--malloy-title-color, #505050);
183
+ border-bottom: 1px solid var(--malloy-border-color, #eaeaeb);
184
+ text-align: left;
185
+ `;
152
186
  const value = field.pivotedField.values[valueIndex];
187
+ headerCell.appendChild(this.document.createTextNode(`${value.field.name}: `));
153
188
  headerCell.appendChild(await this.childRenderers[field.pivotedField.parentField.name].renderChild(value));
154
189
  lastPivotedColumnHash = pfKey;
190
+ row.push(headerCell);
191
+ }
192
+ else {
193
+ row.push(undefined);
155
194
  }
156
- row.push(headerCell);
157
195
  }
158
196
  else {
159
- const headerCell = this.document.createElement('th');
160
- headerCell.style.cssText = `
161
- padding: 8px;
162
- color: var(--malloy-title-color, #505050);
163
- border-bottom: 1px solid var(--malloy-border-color, #eaeaea);
164
- `;
165
- row.push(headerCell);
197
+ row.push(this.createEmptyHeaderCell());
166
198
  }
167
199
  }
168
200
  pivotHeaderCells.push(row);
@@ -215,7 +247,7 @@ class HTMLTableRenderer extends container_1.ContainerRenderer {
215
247
  vertical-align: top;
216
248
  border-bottom: 1px solid var(--malloy-border-color, #eaeaea);
217
249
  width: 25px;
218
- cursor: pointer
250
+ cursor: pointer;
219
251
  `;
220
252
  drillCell.onclick = () => {
221
253
  if (this.options.onDrill) {
@@ -237,7 +269,10 @@ class HTMLTableRenderer extends container_1.ContainerRenderer {
237
269
  }
238
270
  const currentRow = this.document.createElement('tr');
239
271
  for (let column = 0; column < (shouldTranspose ? rowIndex : columnIndex); column++) {
240
- currentRow.appendChild(shouldTranspose ? cells[column][row] : cells[row][column]);
272
+ const cell = shouldTranspose ? cells[column][row] : cells[row][column];
273
+ if (cell) {
274
+ currentRow.appendChild(cell);
275
+ }
241
276
  }
242
277
  tableSection.appendChild(currentRow);
243
278
  }
@@ -258,10 +293,11 @@ class HTMLTableRenderer extends container_1.ContainerRenderer {
258
293
  let dimensions = undefined;
259
294
  const userSpecifiedDimensions = (0, utils_1.parseCommaSeparatedParameterTagValue)(this.tags, 'pivot_dimensions');
260
295
  if (userSpecifiedDimensions) {
261
- dimensions = table.field.allFields.filter(f => userSpecifiedDimensions.indexOf(f.name) >= 0);
296
+ dimensions = table.field.allFieldsWithOrder.filter(f => userSpecifiedDimensions.indexOf(f.field.name) >= 0);
262
297
  if (dimensions.length !== userSpecifiedDimensions.length) {
263
298
  for (const dim of userSpecifiedDimensions) {
264
- if (table.field.allFields.filter(f => f.name === dim).length === 0) {
299
+ if (table.field.allFieldsWithOrder.filter(f => f.field.name === dim)
300
+ .length === 0) {
265
301
  throw new Error(`Could not pivot ${table.field.name} since ${dim} is not a valid field.`);
266
302
  }
267
303
  }
@@ -270,7 +306,7 @@ class HTMLTableRenderer extends container_1.ContainerRenderer {
270
306
  else {
271
307
  dimensions = table.field.dimensions;
272
308
  }
273
- const nonDimensions = table.field.allFields.filter(f => dimensions.indexOf(f) < 0);
309
+ const nonDimensions = table.field.allFieldsWithOrder.filter(f => dimensions.indexOf(f) < 0);
274
310
  if (nonDimensions.length === 0) {
275
311
  throw new Error(`Can not pivot ${table.field.name} since all of its fields are dimensions.`);
276
312
  }
@@ -284,13 +320,13 @@ class HTMLTableRenderer extends container_1.ContainerRenderer {
284
320
  if (!table.isArray() && !table.isRecord()) {
285
321
  throw new Error(`Could not pivot ${table.field.name}`);
286
322
  }
287
- const { dimensions } = this.calculatePivotDimensions(table);
323
+ const { dimensions, nonDimensions } = this.calculatePivotDimensions(table);
288
324
  for (const row of table) {
289
- const pf = new PivotedField(table.field, dimensions.map(f => row.cell(f.name)));
325
+ const pf = new PivotedField(table.field, dimensions.map(f => row.cell(f.field.name)), nonDimensions.length);
290
326
  const renderedCells = new Map();
291
- for (const nonDimension of table.field.allFields.filter(f => dimensions.indexOf(f) < 0)) {
292
- const childRenderer = this.childRenderers[nonDimension.name];
293
- renderedCells.set(nonDimension.name, await this.createCellAndRender(childRenderer, row.cell(nonDimension.name), shouldTranspose));
327
+ for (const nonDimension of table.field.allFieldsWithOrder.filter(f => dimensions.indexOf(f) < 0)) {
328
+ const childRenderer = this.childRenderers[nonDimension.field.name];
329
+ renderedCells.set(nonDimension.field.name, await this.createCellAndRender(childRenderer, row.cell(nonDimension.field.name), shouldTranspose));
294
330
  }
295
331
  if (result.has(pf.key)) {
296
332
  throw new Error(`Can not pivot ${table.field.name} dimensions lead to non unique pivots.`);
@@ -309,6 +345,15 @@ class HTMLTableRenderer extends container_1.ContainerRenderer {
309
345
  cell.appendChild(await childRenderer.render(value));
310
346
  return cell;
311
347
  }
348
+ createEmptyHeaderCell() {
349
+ const headerCell = this.document.createElement('th');
350
+ headerCell.style.cssText = `
351
+ padding: 8px;
352
+ color: var(--malloy-title-color, #505050);
353
+ border-bottom: 1px solid var(--malloy-border-color, #eaeaea);
354
+ `;
355
+ return headerCell;
356
+ }
312
357
  createHeaderCell(field, shouldTranspose) {
313
358
  let name = (0, utils_1.formatTitle)(this.options, field, this.options.dataStyles[field.name], field.parentExplore.queryTimezone);
314
359
  const isNumeric = this.childRenderers[field.name] instanceof number_1.HTMLNumberRenderer;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@malloydata/render",
3
- "version": "0.0.60-dev230718174455",
3
+ "version": "0.0.60",
4
4
  "license": "MIT",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -18,7 +18,7 @@
18
18
  "prepublishOnly": "npm run build"
19
19
  },
20
20
  "dependencies": {
21
- "@malloydata/malloy": "^0.0.60-dev230718174455",
21
+ "@malloydata/malloy": "^0.0.60",
22
22
  "@types/luxon": "^2.4.0",
23
23
  "lodash": "^4.17.20",
24
24
  "luxon": "^2.4.0",