@malloydata/render 0.0.432 → 0.0.434

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.
@@ -0,0 +1,15 @@
1
+ import { RenderPluginFactory, CoreVizPluginInstance } from '../../api/plugin-types';
2
+ import { NestField } from '../../data_tree';
3
+ import { ComboChartSettings } from './get-combo_chart-settings';
4
+ import { ChartDisplayConfig } from '../../component/chart/resolve-chart-display';
5
+ export interface ComboChartPluginInstance extends CoreVizPluginInstance<ComboChartPluginMetadata> {
6
+ field: NestField;
7
+ chartDisplay: ChartDisplayConfig;
8
+ }
9
+ interface ComboChartPluginMetadata {
10
+ type: 'combo';
11
+ field: NestField;
12
+ settings: ComboChartSettings;
13
+ }
14
+ export declare const ComboChartPluginFactory: RenderPluginFactory<ComboChartPluginInstance>;
15
+ export {};
@@ -0,0 +1,66 @@
1
+ import { XChannel, YChannel } from '../../component/types';
2
+ import { JSONSchemaObject, JSONSchemaArray, JSONSchemaString, JSONSchemaNumber, JSONSchemaBoolean, JSONSchemaOneOf } from '../../api/json-schema-types';
3
+ export declare const COMBO_MARK_TYPES: readonly ["bar", "line"];
4
+ export type ComboMarkType = (typeof COMBO_MARK_TYPES)[number];
5
+ export type ComboYChannel = YChannel & {
6
+ chart: ComboMarkType;
7
+ lineWidth?: number;
8
+ showPoints?: boolean;
9
+ min?: number;
10
+ max?: number;
11
+ };
12
+ export interface ComboChartSettings extends Record<string, unknown> {
13
+ xChannel: XChannel;
14
+ yChannel: ComboYChannel;
15
+ y2Channel: ComboYChannel;
16
+ interactive: boolean;
17
+ colorAxes: boolean;
18
+ disableEmbedded: boolean;
19
+ }
20
+ export declare const defaultComboChartSettings: ComboChartSettings;
21
+ export interface IComboChartSettingsSchema extends JSONSchemaObject {
22
+ properties: {
23
+ xChannel: JSONSchemaObject & {
24
+ properties: {
25
+ fields: JSONSchemaArray & {
26
+ items: JSONSchemaString;
27
+ };
28
+ type: JSONSchemaString;
29
+ independent: JSONSchemaString;
30
+ limit: JSONSchemaOneOf;
31
+ };
32
+ };
33
+ yChannel: JSONSchemaObject & {
34
+ properties: {
35
+ fields: JSONSchemaArray & {
36
+ items: JSONSchemaString;
37
+ };
38
+ type: JSONSchemaString;
39
+ independent: JSONSchemaBoolean;
40
+ chart: JSONSchemaString;
41
+ lineWidth: JSONSchemaNumber;
42
+ showPoints: JSONSchemaBoolean;
43
+ min: JSONSchemaNumber;
44
+ max: JSONSchemaNumber;
45
+ };
46
+ };
47
+ y2Channel: JSONSchemaObject & {
48
+ properties: {
49
+ fields: JSONSchemaArray & {
50
+ items: JSONSchemaString;
51
+ };
52
+ type: JSONSchemaString;
53
+ independent: JSONSchemaBoolean;
54
+ chart: JSONSchemaString;
55
+ lineWidth: JSONSchemaNumber;
56
+ showPoints: JSONSchemaBoolean;
57
+ min: JSONSchemaNumber;
58
+ max: JSONSchemaNumber;
59
+ };
60
+ };
61
+ interactive: JSONSchemaBoolean;
62
+ colorAxes: JSONSchemaBoolean;
63
+ disableEmbedded: JSONSchemaBoolean;
64
+ };
65
+ }
66
+ export declare const comboChartSettingsSchema: IComboChartSettingsSchema;
@@ -0,0 +1,9 @@
1
+ import { VegaChartProps } from '../../component/types';
2
+ import { Config } from 'vega';
3
+ import { RenderMetadata } from '../../component/render-result-metadata';
4
+ import { ComboChartPluginInstance } from './combo-chart-plugin';
5
+ /**
6
+ * The slice of the combo chart plugin instance the spec generator reads.
7
+ */
8
+ export type ComboChartSpecInputs = Pick<ComboChartPluginInstance, 'getMetadata' | 'field' | 'chartDisplay'>;
9
+ export declare function generateComboChartVegaSpec(metadata: RenderMetadata, plugin: ComboChartSpecInputs, vegaConfig?: Config): VegaChartProps;
@@ -0,0 +1,5 @@
1
+ import { Tag } from '@malloydata/malloy-tag';
2
+ import { NestField } from '../../data_tree';
3
+ import { ComboChartSettings } from './combo-chart-settings';
4
+ export type { ComboChartSettings };
5
+ export declare function getComboChartSettings(explore: NestField, tagOverride?: Tag): ComboChartSettings;
@@ -0,0 +1,3 @@
1
+ import { Tag } from '@malloydata/malloy-tag';
2
+ import { ComboChartSettings } from './combo-chart-settings';
3
+ export declare function comboChartSettingsToTag(settings: ComboChartSettings): Tag;
@@ -0,0 +1,9 @@
1
+ /**
2
+ * A chart channel's `fields` hold serialized field paths (JSON-encoded arrays).
3
+ * When writing them back into a tag we want the leaf field name, not the full
4
+ * path. Falls back to the raw string for a plain (non-JSON) value.
5
+ *
6
+ * Shared by the bar/line/combo `settings-to-tag` serializers so the extraction
7
+ * lives in one place.
8
+ */
9
+ export declare function extractFieldName(fieldPath: string): string;
@@ -1,5 +1,7 @@
1
1
  export { type ILineChartSettingsSchema, lineChartSettingsSchema, defaultLineChartSettings, type LineChartSettings, } from './line-chart/line-chart-settings';
2
2
  export { type IBarChartSettingsSchema, barChartSettingsSchema, defaultBarChartSettings, type BarChartSettings, } from './bar-chart/bar-chart-settings';
3
3
  export { BarChartPluginFactory, type BarChartPluginInstance, } from './bar-chart/bar-chart-plugin';
4
+ export { type IComboChartSettingsSchema, comboChartSettingsSchema, defaultComboChartSettings, type ComboChartSettings, } from './combo-chart/combo-chart-settings';
5
+ export { ComboChartPluginFactory, type ComboChartPluginInstance, } from './combo-chart/combo-chart-plugin';
4
6
  export { BigValuePluginFactory, type BigValuePluginInstance, type BigValueSettings, type BigValueComparisonInfo, type ComparisonFormat, type BigValueSize, defaultBigValueSettings, bigValueSettingsSchema, type IBigValueSettingsSchema, } from './big-value';
5
7
  export { ErrorPlugin } from './error/error-plugin';
@@ -29,6 +29,22 @@
29
29
  | `# line_chart.series.independent` | Control series legend/color sharing. | Boolean (`true`/`false`). Overrides auto-sharing (default shares if <=20). | `view: my_nest is { # line_chart { series.independent } ... }` |
30
30
  | `# line_chart.title` | Set chart title. | String value. | `view: my_view is { # line_chart { title='Trend Over Time' } ... }` |
31
31
  | `# line_chart.subtitle` | Set chart subtitle. | String value. | `view: my_view is { # line_chart { subtitle='Daily Values' } ... }` |
32
+ | `# combo_chart` | Render two measures on two independent y-axes (dual-axis). | Needs an x dimension and **two** measures. Left axis = `y`, right axis = `y2`. Defaults: `y` bars, `y2` line. The two axes are scaled independently, so where the marks cross is not meaningful. | `view: my_view is { # combo_chart { x=network y=hh_reach y2=conversion_rate } ... }` |
33
+ | `# combo_chart.y` | Measure(s) on the primary (left) axis. | Field ref or array `['m1','m2']`. Auto-filled with the first measure if omitted. | `view: my_view is { # combo_chart { y=hh_reach } ... }` |
34
+ | `# combo_chart.y2` | Measure(s) on the secondary (right) axis. | Field ref or array. Auto-filled with the second measure if omitted. | `view: my_view is { # combo_chart { y2=conversion_rate } ... }` |
35
+ | `# combo_chart.y.chart` | Mark type for the left axis. | `bar` or `line`. Default `bar`. | `view: my_view is { # combo_chart { y.chart=line } ... }` |
36
+ | `# combo_chart.y2.chart` | Mark type for the right axis. | `bar` or `line`. Default `line`. Set `y.chart`/`y2.chart` to get bar-over-line or line-over-bar. Both `bar` → grouped side-by-side, not stacked. | `view: my_view is { # combo_chart { y2.chart=bar } ... }` |
37
+ | `# combo_chart.y2.line_width` | Line stroke width (px) for a line axis. | Number, default `2`. Also `y.line_width`. Ignored for bar axes. | `view: my_view is { # combo_chart { y2.line_width=3 } ... }` |
38
+ | `# combo_chart.y2.points` | Show/hide dots on a line axis. | `true`/`false`; default auto (dots hidden for multi-point series). Also `y.points`. Ignored for bars. | `view: my_view is { # combo_chart { y2.points=false } ... }` |
39
+ | `# combo_chart.y2.min` / `.y2.max`| Pin an axis's domain bounds. | Number; either end alone. Also `y.min`/`y.max`. Pin both axes to align the crossover. | `view: my_view is { # combo_chart { y.min=0 y2.min=0 y2.max=100 } ... }` |
40
+ | `# combo_chart.x` | Define x-axis field. | Assigns a field to the category axis. Auto-detected (time or first dimension) if omitted. | `view: my_view is { # combo_chart { x=network } ... }` |
41
+ | `# combo_chart.x.limit` | Limit x-axis bands. | Numeric value; auto-fit to plot width if unset. Notes "Showing N of M" when bands are dropped. | `view: my_view is { # combo_chart { x.limit=20 } ... }` |
42
+ | `# combo_chart.color_axes` | Tint each axis to its mark's color. | Boolean, default `true`. Colors the axis title/ticks/line so the two scales read as separate rulers. Set `false` for neutral axes. | `view: my_view is { # combo_chart { color_axes=false } ... }` |
43
+ | `# combo_chart.y.independent` | Control left-axis scale sharing. | Boolean. Independent domains across nested chart rows. | `view: my_nest is { # combo_chart { y.independent } ... }` |
44
+ | `# combo_chart.y2.independent` | Control right-axis scale sharing. | Boolean. Independent domains across nested chart rows. | `view: my_nest is { # combo_chart { y2.independent } ... }` |
45
+ | `# combo_chart.size` | Set preset chart size. | Values: `spark`, `xs`...`2xl`, or `size.width` / `size.height` in pixels. | `view: my_view is { # combo_chart { size=lg } ... }` |
46
+ | `# combo_chart.title` | Set chart title. | String value. | `view: my_view is { # combo_chart { title='Reach vs. Rate' } ... }` |
47
+ | `# combo_chart.subtitle` | Set chart subtitle. | String value. | `view: my_view is { # combo_chart { subtitle='By network' } ... }` |
32
48
  | `# scatter_chart` | Render as scatter plot. | Legacy renderer. Uses field order (x, y, color, size, shape). | `view: my_view is { # scatter_chart ... }` |
33
49
  | **Layout & Structure** | | | |
34
50
  | `# dashboard` | Render nested views as a dashboard. | Base tag for dashboard layout. | `view: my_dashboard is { # dashboard nest: ... }` |
@@ -136,6 +136,58 @@ aggregate: daily_sales
136
136
  }
137
137
  ```
138
138
 
139
+ ### `# combo_chart`
140
+
141
+ Renders two measures on two **independent** y-axes (a dual-axis chart), for the
142
+ classic "bars + line" comparison of measures with different units or magnitudes
143
+ (e.g. volume vs. rate, count vs. dollars). Requires an x dimension and **two**
144
+ measures.
145
+
146
+ **Properties:**
147
+
148
+ - `.y`: Measure(s) on the primary (left) axis. A field ref or array `['m1','m2']`. Auto-filled with the first measure if omitted.
149
+ - Syntax: `# combo_chart { y=hh_reach }`
150
+ - `.y2`: Measure(s) on the secondary (right) axis. Auto-filled with the second measure if omitted.
151
+ - Syntax: `# combo_chart { y2=conversion_rate }`
152
+ - `.y.chart` / `.y2.chart`: Mark type for each axis — `bar` or `line`. Defaults: `y` is `bar`, `y2` is `line`. Assign the marks to get bar-over-line or line-over-bar. Setting **both** axes to `bar` is supported: the two groups are drawn side-by-side within each band (each keeps its own y-scale), never stacked on top of each other.
153
+ - Syntax: `# combo_chart { y.chart=line y2.chart=bar }`
154
+ - `.y.line_width` / `.y2.line_width`: Stroke width in px for a line axis (default `2`). Ignored for bar axes.
155
+ - Syntax: `# combo_chart { y2.line_width=3 }`
156
+ - `.y.points` / `.y2.points`: Show point markers on a line. Default is auto — dots are hidden once a series has more than one point (matching `# line_chart`), shown for a single-point series or on hover. Set `points=true` to always show dots, `points=false` to always hide them. Ignored for bar axes.
157
+ - Syntax: `# combo_chart { y2.points=false }`
158
+ - `.x`, `.title`, `.subtitle`, `.size`: Similar to `# bar_chart`.
159
+ - `.x.limit`: Cap the number of x-axis bands. A number pins it; omit for an auto limit that fits the plot width. When bands are dropped the chart notes "Showing N of M".
160
+ - Syntax: `# combo_chart { x.limit=20 }`
161
+ - `.color_axes`: Whether each axis is tinted to its mark's color (default `true`). Set `false` for plain, neutral axes.
162
+ - Syntax: `# combo_chart { color_axes=false }`
163
+ - `.y.independent` / `.y2.independent`: Controls each axis's domain sharing across nested charts.
164
+ - `.y.min` / `.y.max` / `.y2.min` / `.y2.max`: Pin an axis's domain bounds. Either end can be pinned on its own; the other still comes from the data. Pinning both axes to comparable ranges is how you defuse the misleading-crossover pitfall below.
165
+ - Syntax: `# combo_chart { y.min=0 y2.min=0 y2.max=100 }`
166
+
167
+ **Axis scaling:** each axis is scaled independently from its own measures (the
168
+ left/bar axis includes zero). Because the two scales are independent, where the
169
+ bars and line cross is an artifact of the chosen ranges, not the data. To keep
170
+ this from misleading, each axis is **tinted to its mark's color** (left axis like
171
+ the bars, right axis like the line) so the two scales read as two separate
172
+ rulers rather than a shared one. Still: use the chart to compare *trends*, not
173
+ absolute crossover points — or pin the axes with `y.min`/`y.max`/`y2.min`/`y2.max`
174
+ to make the crossover meaningful.
175
+
176
+ **Examples:**
177
+
178
+ ```
179
+ // Smart defaults: first measure → bars (left), second → line (right)
180
+ # combo_chart
181
+ view: reach_and_rate is {
182
+ group_by: network
183
+ aggregate: hh_reach, conversion_rate
184
+ }
185
+
186
+ // Explicit, with swapped marks (line on left, bars on right)
187
+ # combo_chart { x=network y=cpa y.chart=line y2=volume y2.chart=bar }
188
+ view: cpa_and_volume is { ... }
189
+ ```
190
+
139
191
  ### `# scatter_chart`
140
192
 
141
193
  _Note: This currently uses the legacy renderer._
@@ -1,3 +1,5 @@
1
+ # Config format: node ../../scripts/femto-build.js --help
2
+
1
3
  build: {
2
4
  inputs = [
3
5
  "src/**/*.ts",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@malloydata/render",
3
- "version": "0.0.432",
3
+ "version": "0.0.434",
4
4
  "license": "MIT",
5
5
  "main": "dist/module/index.umd.js",
6
6
  "types": "dist/module/index.d.ts",
@@ -31,8 +31,8 @@
31
31
  "generate-flow": "ts-node ../../scripts/gen-flow.ts"
32
32
  },
33
33
  "dependencies": {
34
- "@malloydata/malloy-interfaces": "0.0.432",
35
- "@malloydata/malloy-tag": "0.0.432",
34
+ "@malloydata/malloy-interfaces": "0.0.434",
35
+ "@malloydata/malloy-tag": "0.0.434",
36
36
  "@tanstack/solid-virtual": "^3.13.33",
37
37
  "lodash": "^4.18.1",
38
38
  "luxon": "^3.7.2",
@@ -44,8 +44,8 @@
44
44
  "vega-lite": "^5.2.0"
45
45
  },
46
46
  "devDependencies": {
47
- "@malloydata/db-duckdb": "0.0.432",
48
- "@malloydata/malloy": "0.0.432",
47
+ "@malloydata/db-duckdb": "0.0.434",
48
+ "@malloydata/malloy": "0.0.434",
49
49
  "@storybook/addon-essentials": "^8.6.18",
50
50
  "@storybook/addon-interactions": "^8.6.18",
51
51
  "@storybook/addon-links": "^8.6.15",