@malloydata/render 0.0.323 → 0.0.325
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/CONTEXT.md +93 -0
- package/dist/module/component/table/pivot-cells.d.ts +13 -0
- package/dist/module/component/table/pivot-header.d.ts +22 -0
- package/dist/module/component/table/pivot-utils.d.ts +127 -0
- package/dist/module/component/table/table-context.d.ts +3 -0
- package/dist/module/component/table/table-layout.d.ts +10 -0
- package/dist/module/component/table/transpose-table.d.ts +16 -0
- package/dist/module/index.mjs +43155 -33685
- package/dist/module/index.umd.js +572 -505
- package/package.json +9 -9
package/CONTEXT.md
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# Malloy Render
|
|
2
|
+
|
|
3
|
+
The `malloy-render` package handles visualization and rendering of Malloy query results. It transforms query results into rich, interactive visualizations and tables.
|
|
4
|
+
|
|
5
|
+
## Purpose
|
|
6
|
+
|
|
7
|
+
Malloy queries return structured data with metadata about how it should be displayed. The render package:
|
|
8
|
+
- Interprets query result metadata
|
|
9
|
+
- Generates appropriate visualizations
|
|
10
|
+
- Provides interactive data exploration
|
|
11
|
+
- Supports multiple rendering formats
|
|
12
|
+
|
|
13
|
+
## Technology Stack
|
|
14
|
+
|
|
15
|
+
### Solid.js
|
|
16
|
+
The rendering system uses **Solid.js** as its reactive UI framework.
|
|
17
|
+
|
|
18
|
+
### Vega
|
|
19
|
+
The package uses **Vega** for declarative data visualizations.
|
|
20
|
+
|
|
21
|
+
## Rendering Process
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
Query Results + Metadata → Renderer → Visualization Selection → Vega Spec / Table → UI Output
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
**Steps:**
|
|
28
|
+
1. Query results arrive with rendering metadata
|
|
29
|
+
2. Renderer examines result structure and annotations
|
|
30
|
+
3. Appropriate visualization type is selected
|
|
31
|
+
4. Vega specification is generated (for charts) or table is formatted
|
|
32
|
+
5. UI components render the visualization
|
|
33
|
+
|
|
34
|
+
## Rendering Hints
|
|
35
|
+
|
|
36
|
+
The renderer respects annotations and metadata from Malloy models:
|
|
37
|
+
|
|
38
|
+
- **Renderer annotations** (prefix `# `) provide display hints
|
|
39
|
+
- **Field types** influence default visualizations
|
|
40
|
+
- **Data structure** (nested, flat, aggregated) affects rendering choices
|
|
41
|
+
- **User preferences** can override defaults
|
|
42
|
+
|
|
43
|
+
## Visualization Types
|
|
44
|
+
|
|
45
|
+
The renderer supports various output formats:
|
|
46
|
+
|
|
47
|
+
- **Tables** - Default for most query results
|
|
48
|
+
- **Bar charts** - For categorical comparisons
|
|
49
|
+
- **Line charts** - For time series data
|
|
50
|
+
- **Scatter plots** - For correlation analysis
|
|
51
|
+
- **Nested tables** - For hierarchical data
|
|
52
|
+
- **Sparklines** - For inline visualizations
|
|
53
|
+
- And more...
|
|
54
|
+
|
|
55
|
+
## Package Integration
|
|
56
|
+
|
|
57
|
+
The render package integrates with:
|
|
58
|
+
- **malloy-core** - Consumes query results and metadata
|
|
59
|
+
- **VS Code extension** - Provides visualization in editor
|
|
60
|
+
- **Web applications** - Can be embedded in web apps
|
|
61
|
+
- **Reports** - Generates static visualizations
|
|
62
|
+
|
|
63
|
+
## Architecture
|
|
64
|
+
|
|
65
|
+
### Two Renderers
|
|
66
|
+
- **New renderer** (`src/component/`) - Solid.js-based, the default
|
|
67
|
+
- **Legacy renderer** (`src/html/`) - HTML string-based, activated with `## renderer_legacy` model tag
|
|
68
|
+
|
|
69
|
+
### Tag System
|
|
70
|
+
Render annotations use the Malloy Tag API to check for rendering hints. For tag language syntax, see [packages/malloy-tag/CONTEXT.md](../malloy-tag/CONTEXT.md).
|
|
71
|
+
|
|
72
|
+
Common API patterns in the renderer:
|
|
73
|
+
- `field.tag.has('pivot')` - Check if a tag exists
|
|
74
|
+
- `field.tag.text('label')` - Get a text property
|
|
75
|
+
- `field.tag.textArray('pivot', 'dimensions')` - Get array property with path
|
|
76
|
+
- `field.tag.tag('table', 'size')` - Navigate nested tag properties
|
|
77
|
+
|
|
78
|
+
### Table Layout
|
|
79
|
+
The table uses CSS Grid with subgrid. Layout is calculated in `table-layout.ts`:
|
|
80
|
+
- `getTableLayout()` - Creates base layout from field structure
|
|
81
|
+
- `adjustLayoutForPivots()` - Adjusts column counts for pivot expansion
|
|
82
|
+
- Column ranges are tracked for each field to support nested tables
|
|
83
|
+
|
|
84
|
+
### Testing
|
|
85
|
+
Use Storybook (`npm run storybook`) to test visual changes. Stories are in `src/stories/*.stories.malloy`.
|
|
86
|
+
|
|
87
|
+
## Important Notes
|
|
88
|
+
|
|
89
|
+
- Rendering is **separate from query execution** - it only processes results
|
|
90
|
+
- The renderer is **stateless** - same results produce same visualizations
|
|
91
|
+
- Visualizations respect **user accessibility** preferences where possible
|
|
92
|
+
- The package is designed to be **embeddable** in various contexts
|
|
93
|
+
- **Never run the full test suite** without restrictions - use targeted tests or storybook
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { JSX } from 'solid-js';
|
|
2
|
+
import { RecordCell } from '../../data_tree';
|
|
3
|
+
import { PivotConfig } from './pivot-utils';
|
|
4
|
+
/**
|
|
5
|
+
* Renders all pivot cells for one row's nested pivot field.
|
|
6
|
+
*/
|
|
7
|
+
export declare const PivotFieldCells: (props: {
|
|
8
|
+
row: RecordCell;
|
|
9
|
+
pivotConfig: PivotConfig;
|
|
10
|
+
rowPath: number[];
|
|
11
|
+
startColumn: number;
|
|
12
|
+
}) => JSX.Element;
|
|
13
|
+
export default PivotFieldCells;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Field } from '../../data_tree';
|
|
2
|
+
import { PivotConfig } from './pivot-utils';
|
|
3
|
+
/**
|
|
4
|
+
* Info about a sibling field (non-pivot field at same depth as pivot).
|
|
5
|
+
*/
|
|
6
|
+
export type SiblingFieldInfo = {
|
|
7
|
+
field: Field;
|
|
8
|
+
startColumn: number;
|
|
9
|
+
endColumn: number;
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Renders all header rows for a pivot field.
|
|
13
|
+
* Row 1: Empty for siblings + dimension values with colspan
|
|
14
|
+
* Row 2: Sibling headers + measure names repeated
|
|
15
|
+
*/
|
|
16
|
+
export declare const PivotHeaders: (props: {
|
|
17
|
+
pivotConfig: PivotConfig;
|
|
18
|
+
pivotStartColumn: number;
|
|
19
|
+
totalColumns: number;
|
|
20
|
+
siblingFields: SiblingFieldInfo[];
|
|
21
|
+
}) => import("solid-js").JSX.Element;
|
|
22
|
+
export default PivotHeaders;
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { Cell, Field, NestField, RecordOrRepeatedRecordCell, RecordOrRepeatedRecordField, SortableField } from '../../data_tree';
|
|
2
|
+
/**
|
|
3
|
+
* Represents a unique combination of dimension values in a pivot.
|
|
4
|
+
* Each PivotedField corresponds to one set of column headers.
|
|
5
|
+
*/
|
|
6
|
+
export type PivotedField = {
|
|
7
|
+
/** JSON-stringified key for lookup (includes parent field name and dimension values) */
|
|
8
|
+
key: string;
|
|
9
|
+
/** The dimension cell values for this pivot combination */
|
|
10
|
+
values: Cell[];
|
|
11
|
+
/** The parent nested field being pivoted */
|
|
12
|
+
parentField: RecordOrRepeatedRecordField;
|
|
13
|
+
/** Number of non-dimension (measure) columns this spans */
|
|
14
|
+
span: number;
|
|
15
|
+
/** Map from dimension field name to cell value for quick lookup */
|
|
16
|
+
fieldValueMap: Map<string, Cell>;
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Represents a single column in a pivoted table.
|
|
20
|
+
* Combines a specific dimension value combination with a measure field.
|
|
21
|
+
*/
|
|
22
|
+
export type PivotedColumnField = {
|
|
23
|
+
/** The pivot dimension combination */
|
|
24
|
+
pivotedField: PivotedField;
|
|
25
|
+
/** The actual measure field to render */
|
|
26
|
+
field: Field;
|
|
27
|
+
/** User-defined dimension fields (if specified) */
|
|
28
|
+
userDefinedPivotDimensions?: string[];
|
|
29
|
+
};
|
|
30
|
+
export type { SortableField };
|
|
31
|
+
/**
|
|
32
|
+
* Complete configuration for a pivot field.
|
|
33
|
+
*/
|
|
34
|
+
export type PivotConfig = {
|
|
35
|
+
/** The nested field being pivoted */
|
|
36
|
+
field: NestField;
|
|
37
|
+
/** Dimension fields (used for column headers) */
|
|
38
|
+
dimensions: SortableField[];
|
|
39
|
+
/** Non-dimension fields (measures to display) */
|
|
40
|
+
nonDimensions: SortableField[];
|
|
41
|
+
/** All unique dimension value combinations, sorted */
|
|
42
|
+
pivotedFields: PivotedField[];
|
|
43
|
+
/** Expanded column list: pivotedFields * nonDimensions */
|
|
44
|
+
columnFields: PivotedColumnField[];
|
|
45
|
+
/** Max number of dimension levels (for header row depth) */
|
|
46
|
+
pivotDepth: number;
|
|
47
|
+
};
|
|
48
|
+
/** Maximum number of pivot columns allowed */
|
|
49
|
+
export declare const PIVOT_COLUMN_LIMIT = 30;
|
|
50
|
+
/** Maximum number of transpose columns allowed */
|
|
51
|
+
export declare const TRANSPOSE_COLUMN_LIMIT = 20;
|
|
52
|
+
/**
|
|
53
|
+
* Creates a unique key for a pivot dimension combination.
|
|
54
|
+
*/
|
|
55
|
+
export declare function createPivotKey(parentField: RecordOrRepeatedRecordField, values: Cell[]): string;
|
|
56
|
+
/**
|
|
57
|
+
* Separates fields into dimensions and non-dimensions (measures).
|
|
58
|
+
*
|
|
59
|
+
* @param field The nested field being pivoted
|
|
60
|
+
* @param userDimensions Optional explicit dimension field names
|
|
61
|
+
* @returns Object with dimensions and nonDimensions arrays
|
|
62
|
+
*/
|
|
63
|
+
export declare function calculatePivotDimensions(field: NestField, userDimensions?: string[]): {
|
|
64
|
+
dimensions: SortableField[];
|
|
65
|
+
nonDimensions: SortableField[];
|
|
66
|
+
};
|
|
67
|
+
/**
|
|
68
|
+
* Collects all unique dimension value combinations across all rows of data.
|
|
69
|
+
*
|
|
70
|
+
* @param field The nested field being pivoted
|
|
71
|
+
* @param data The parent table data
|
|
72
|
+
* @param dimensions The dimension fields to extract values from
|
|
73
|
+
* @returns Map of pivot keys to PivotedField objects
|
|
74
|
+
*/
|
|
75
|
+
export declare function collectPivotedFields(field: NestField, data: RecordOrRepeatedRecordCell, dimensions: SortableField[], nonDimensionCount: number): Map<string, PivotedField>;
|
|
76
|
+
/**
|
|
77
|
+
* Sorts pivoted fields by their dimension values.
|
|
78
|
+
* Uses Cell.compareTo() for type-appropriate sorting.
|
|
79
|
+
*
|
|
80
|
+
* @param pivotedFields Array of PivotedField objects
|
|
81
|
+
* @param dimensions Dimension fields with sort direction
|
|
82
|
+
* @returns Sorted array
|
|
83
|
+
*/
|
|
84
|
+
export declare function sortPivotedFields(pivotedFields: PivotedField[], dimensions: SortableField[]): PivotedField[];
|
|
85
|
+
/**
|
|
86
|
+
* Expands sorted pivot fields into individual column fields.
|
|
87
|
+
* Each pivot combination * each non-dimension = one column.
|
|
88
|
+
*
|
|
89
|
+
* @param pivotedFields Sorted array of PivotedField objects
|
|
90
|
+
* @param nonDimensions Non-dimension (measure) fields
|
|
91
|
+
* @param userDimensions Optional user-specified dimension names
|
|
92
|
+
* @returns Array of PivotedColumnField objects
|
|
93
|
+
*/
|
|
94
|
+
export declare function expandPivotColumns(pivotedFields: PivotedField[], nonDimensions: SortableField[], userDimensions?: string[]): PivotedColumnField[];
|
|
95
|
+
/**
|
|
96
|
+
* Builds a complete pivot configuration for a nested field.
|
|
97
|
+
*
|
|
98
|
+
* @param field The nested field with # pivot tag
|
|
99
|
+
* @param data The parent table data
|
|
100
|
+
* @param userDimensions Optional explicit dimension field names
|
|
101
|
+
* @returns PivotConfig or null if pivot cannot be created
|
|
102
|
+
*/
|
|
103
|
+
export declare function buildPivotConfig(field: NestField, data: RecordOrRepeatedRecordCell, userDimensions?: string[]): PivotConfig;
|
|
104
|
+
/**
|
|
105
|
+
* Generates a map of pivot keys to cell values for a single row's nested data.
|
|
106
|
+
* Used during rendering to look up the correct cell for each pivot column.
|
|
107
|
+
*
|
|
108
|
+
* @param nestedCell The nested record data for one row
|
|
109
|
+
* @param pivotConfig The pivot configuration
|
|
110
|
+
* @returns Map from pivot key to map of field name to cell
|
|
111
|
+
*/
|
|
112
|
+
export declare function generatePivotedCellsMap(nestedCell: RecordOrRepeatedRecordCell, pivotConfig: PivotConfig): Map<string, Map<string, Cell>>;
|
|
113
|
+
/**
|
|
114
|
+
* Gets the pivot dimension values to display in a cell.
|
|
115
|
+
* Used for rendering dimension value header cells.
|
|
116
|
+
*/
|
|
117
|
+
export declare function getPivotDimensionValue(pivotedField: PivotedField, dimensionIndex: number): Cell | undefined;
|
|
118
|
+
/**
|
|
119
|
+
* Checks if a field should be rendered as a pivot table.
|
|
120
|
+
* Syntax: # pivot
|
|
121
|
+
*/
|
|
122
|
+
export declare function shouldPivot(field: Field): boolean;
|
|
123
|
+
/**
|
|
124
|
+
* Gets user-defined pivot dimensions from the tag.
|
|
125
|
+
* Syntax: # pivot { dimensions=[d1, d2] }
|
|
126
|
+
*/
|
|
127
|
+
export declare function getUserDefinedDimensions(field: Field): string[] | undefined;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { SetStoreFunction, Store, createStore } from 'solid-js/store';
|
|
2
2
|
import { TableLayout } from './table-layout';
|
|
3
|
+
import { PivotConfig } from './pivot-utils';
|
|
3
4
|
type TableStore = {
|
|
4
5
|
headerSizes: Record<string, number>;
|
|
5
6
|
columnWidths: Record<string, number>;
|
|
@@ -15,6 +16,8 @@ export type TableContext = {
|
|
|
15
16
|
headerSizeStore: ReturnType<typeof createStore<Record<string, number>>>;
|
|
16
17
|
currentRow: number[];
|
|
17
18
|
currentExplore: string[];
|
|
19
|
+
/** Map of field key to pivot configuration */
|
|
20
|
+
pivotConfigs: Map<string, PivotConfig>;
|
|
18
21
|
};
|
|
19
22
|
export declare const TableContext: import('solid-js').Context<TableContext | undefined>;
|
|
20
23
|
export declare const useTableContext: () => TableContext | undefined;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Field, NestField } from '../../data_tree';
|
|
2
2
|
import { FieldHeaderRangeMap } from '../types';
|
|
3
|
+
import { PivotConfig } from './pivot-utils';
|
|
3
4
|
type LayoutEntry = {
|
|
4
5
|
field: Field;
|
|
5
6
|
width: number | null;
|
|
@@ -16,4 +17,13 @@ export type TableLayout = {
|
|
|
16
17
|
maxDepth: number;
|
|
17
18
|
};
|
|
18
19
|
export declare function getTableLayout(rootField: NestField): TableLayout;
|
|
20
|
+
/**
|
|
21
|
+
* Adjusts a table layout to account for pivot column expansion.
|
|
22
|
+
* Pivot fields may have more columns than their original nested structure.
|
|
23
|
+
*
|
|
24
|
+
* @param layout The original table layout
|
|
25
|
+
* @param pivotConfigs Map of field keys to their pivot configurations
|
|
26
|
+
* @returns Adjusted layout with correct column counts for pivot fields
|
|
27
|
+
*/
|
|
28
|
+
export declare function adjustLayoutForPivots(layout: TableLayout, pivotConfigs: Map<string, PivotConfig>): TableLayout;
|
|
19
29
|
export {};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Component } from 'solid-js';
|
|
2
|
+
import { RecordOrRepeatedRecordCell, Field } from '../../data_tree';
|
|
3
|
+
/**
|
|
4
|
+
* TransposeTable renders a table with rows and columns swapped.
|
|
5
|
+
* Each field becomes a row, and each data record becomes a column.
|
|
6
|
+
*/
|
|
7
|
+
declare const TransposeTable: Component<{
|
|
8
|
+
data: RecordOrRepeatedRecordCell;
|
|
9
|
+
rowLimit?: number;
|
|
10
|
+
}>;
|
|
11
|
+
export default TransposeTable;
|
|
12
|
+
/**
|
|
13
|
+
* Checks if a field should be rendered as a transposed table.
|
|
14
|
+
* Syntax: # transpose
|
|
15
|
+
*/
|
|
16
|
+
export declare function shouldTranspose(field: Field): boolean;
|