Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions web-common/src/features/canvas/Toolbar.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@
"donut_chart",
"pie_chart",
"heatmap",
"combo_chart",
"custom_chart",
] as const;

$: showExplore =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import type {
TimeAndFilterStore,
TimeRangeState,
} from "../../dashboards/time-controls/time-control-store";
import { TimeRangePreset } from "@rilldata/web-common/lib/time/types";
import type {
CanvasEntity,
ComponentPath,
Expand Down Expand Up @@ -168,11 +169,13 @@ export abstract class BaseCanvasComponent<T = ComponentSpec> {
this.parent.timeManager.state.comparisonRangeStore,
this.parent.timeManager.state.comparisonIntervalStore,
this.parent.timeManager.state.timeZoneStore,
this.parent.timeManager.state.rangeStore,
this.localTimeControls.interval,
this.localTimeControls.comparisonIntervalStore,
this.localTimeControls.showTimeComparisonStore,
this.localTimeControls.grainStore,
this.localTimeControls.comparisonRangeStore,
this.localTimeControls.rangeStore,
this.parent.filterManager.metricsViewFilters,
this.parent.specStore,
this.parent.timeManager.hasTimeSeriesMap,
Expand All @@ -186,11 +189,13 @@ export abstract class BaseCanvasComponent<T = ComponentSpec> {
globalComparisonRange,
globalComparisonInterval,
timeZone,
globalRange,
localInterval,
localComparisonInterval,
localShowTimeComparison,
localGrainStore,
localComparisonRange,
localRange,
metricsViewFilters,
canvasData,
hasTimeSeriesMap,
Expand All @@ -212,6 +217,14 @@ export abstract class BaseCanvasComponent<T = ComponentSpec> {
};

let timeRangeState: TimeRangeState | undefined = {
selectedTimeRange: globalInterval
? {
name: globalRange ?? TimeRangePreset.CUSTOM,
start: globalInterval.start.toJSDate(),
end: globalInterval.end.toJSDate(),
interval: globalGrainStore,
}
: undefined,
timeStart: globalInterval?.start.toISO(),
timeEnd: globalInterval?.end.toISO(),
};
Expand Down Expand Up @@ -273,6 +286,14 @@ export abstract class BaseCanvasComponent<T = ComponentSpec> {
timeGrain = localGrainStore ?? globalGrainStore;

const localTimeRangeState: TimeRangeState = {
selectedTimeRange: localInterval
? {
name: localRange ?? TimeRangePreset.CUSTOM,
start: localInterval.start.toJSDate(),
end: localInterval.end.toJSDate(),
interval: localGrainStore ?? globalGrainStore,
}
: undefined,
timeStart: localInterval?.start.toISO(),
timeEnd: localInterval?.end.toISO(),
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,10 +129,17 @@ export abstract class BaseChart<
const timeGrain = get(this.timeAndFilterStore)?.timeGrain;
const tddLink = getLinkStateForTimeDimensionDetail(spec, this.type);

const comparisonChartTypes = [
"bar_chart",
"stacked_bar",
"stacked_bar_normalized",
];
const passComparison = comparisonChartTypes.includes(this.type);

return {
whereFilter: dimensionFilters,
dimensionThresholdFilters,
showTimeComparison: false,
...(passComparison ? {} : { showTimeComparison: false }),
activePage: tddLink.canLink
? DashboardState_ActivePage.TIME_DIMENSIONAL_DETAIL
: DashboardState_ActivePage.PIVOT,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import { onDestroy } from "svelte";
import AgenticChartPrompt from "./AgenticChartPrompt.svelte";
import { clearComponentConversation } from "./chart-ai-agent";
import type { CustomChartComponent } from "./index";
import type { CustomChartComponent, QueryFieldMeta } from "./index";

export let component: CustomChartComponent;
export let editable: boolean = false;
Expand All @@ -16,6 +16,14 @@

$: hasValidSpec = component.isValid($specStore);
$: hasContent = component.hasContent($specStore);

function handleMetaChange(meta: Record<string, unknown> | undefined) {
if (!meta?.fields || !Array.isArray(meta.fields)) {
component.queryFieldsMeta.set([]);
return;
}
component.queryFieldsMeta.set(meta.fields as QueryFieldMeta[]);
}
</script>

{#if hasValidSpec || hasContent}
Expand All @@ -26,6 +34,7 @@
timeRange={$timeAndFilterStore?.timeRange}
metricsSQL={$specStore.metrics_sql}
showDataTable={editable}
onMetaChange={handleMetaChange}
/>
{:else}
<AgenticChartPrompt {component} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,16 @@ import type {
CanvasEntity,
ComponentPath,
} from "@rilldata/web-common/features/canvas/stores/canvas-entity";
import {
PivotChipType,
type PivotChipData,
type PivotState,
} from "@rilldata/web-common/features/dashboards/pivot/types";
import type { ExploreState } from "@rilldata/web-common/features/dashboards/stores/explore-state";
import { splitWhereFilter } from "@rilldata/web-common/features/dashboards/filters/measure-filters/measure-filter-utils";
import { DashboardState_ActivePage } from "@rilldata/web-common/proto/gen/rill/ui/v1/dashboard_pb";
import type { V1Resource } from "@rilldata/web-common/runtime-client";
import { get } from "svelte/store";
import { get, writable, type Writable } from "svelte/store";
import CanvasCustomChart from "./CanvasCustomChart.svelte";

export interface CustomChart
Expand All @@ -23,12 +31,19 @@ export interface CustomChart
vega_spec: string;
}

export interface QueryFieldMeta {
type: "dimension" | "measure";
name: string;
display_name?: string;
}

export class CustomChartComponent extends BaseCanvasComponent<CustomChart> {
minSize = { width: 4, height: 4 };
defaultSize = { width: 6, height: 4 };
resetParams = [];
type: CanvasComponentType = "custom_chart";
component = CanvasCustomChart;
queryFieldsMeta: Writable<QueryFieldMeta[]> = writable([]);

constructor(resource: V1Resource, parent: CanvasEntity, path: ComponentPath) {
const defaultSpec: CustomChart = {
Expand Down Expand Up @@ -93,6 +108,54 @@ export class CustomChartComponent extends BaseCanvasComponent<CustomChart> {
);
}

getExploreTransformerProperties(): Partial<ExploreState> {
const fields = get(this.queryFieldsMeta);
const timeAndFilter = get(this.timeAndFilterStore);

const { dimensionFilters, dimensionThresholdFilters } = splitWhereFilter(
timeAndFilter?.where,
);

const columns: PivotChipData[] = [];
const rows: PivotChipData[] = [];

for (const field of fields) {
if (field.type === "measure") {
columns.push({
id: field.name,
title: field.display_name ?? field.name,
type: PivotChipType.Measure,
});
} else {
rows.push({
id: field.name,
title: field.display_name ?? field.name,
type: PivotChipType.Dimension,
});
}
}

const pivot: PivotState = {
columns,
rows,
expanded: {},
sorting: [],
columnPage: 0,
rowPage: 0,
enableComparison: false,
tableMode: "nest",
activeCell: null,
};

return {
whereFilter: dimensionFilters,
dimensionThresholdFilters,
showTimeComparison: false,
activePage: DashboardState_ActivePage.PIVOT,
pivot,
};
}

inputParams(): InputParams<CustomChart> {
return {
options: {
Expand Down
37 changes: 21 additions & 16 deletions web-common/src/features/canvas/components/charts/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,36 +28,41 @@ export function getLinkStateForTimeDimensionDetail(
dimensionName?: string;
} {
if (!allowedTimeDimensionDetailTypes.includes(type))
return {
canLink: false,
};
return { canLink: false };

const hasXAxis = "x" in spec;
const hasYAxis = "y" in spec;
if (!hasXAxis || !hasYAxis)
return {
canLink: false,
};
return { canLink: false };

const xAxis = spec.x;
const yAxis = spec.y;

if (isFieldConfig(xAxis) && isFieldConfig(yAxis)) {
const colorDimension = spec.color;
if (isFieldConfig(colorDimension)) {
return {
canLink: xAxis.type === "temporal",
measureName: yAxis.field,
dimensionName: colorDimension.field,
};
}
if (!isFieldConfig(xAxis) || !isFieldConfig(yAxis))
return { canLink: false };

if (yAxis.fields && yAxis.fields.length > 1)
return { canLink: false };

const colorDimension = spec.color;
const hasDimensionBreakout =
isFieldConfig(colorDimension) &&
colorDimension.type !== "quantitative" &&
colorDimension.type !== "value";

if (hasDimensionBreakout && xAxis.type === "nominal")
return { canLink: false };

if (hasDimensionBreakout) {
return {
canLink: xAxis.type === "temporal",
measureName: yAxis.field,
dimensionName: colorDimension.field,
};
}

return {
canLink: false,
canLink: xAxis.type === "temporal",
measureName: yAxis.field,
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,21 @@ import {
type ChartFieldsMap,
type FieldConfig,
} from "@rilldata/web-common/features/components/charts/types";
import { splitWhereFilter } from "@rilldata/web-common/features/dashboards/filters/measure-filters/measure-filter-utils";
import {
PivotChipType,
type PivotChipData,
type PivotState,
} from "@rilldata/web-common/features/dashboards/pivot/types";
import type { ExploreState } from "@rilldata/web-common/features/dashboards/stores/explore-state";
import type { TimeAndFilterStore } from "@rilldata/web-common/features/dashboards/time-controls/time-control-store";
import { DashboardState_ActivePage } from "@rilldata/web-common/proto/gen/rill/ui/v1/dashboard_pb";
import {
MetricsViewSpecDimensionType,
type V1MetricsViewSpec,
type V1Resource,
} from "@rilldata/web-common/runtime-client";
import { V1TimeGrain } from "@rilldata/web-common/runtime-client";
import { get, type Readable } from "svelte/store";
import type { ChartDataQuery } from "../../../../components/charts/types";
import type {
Expand Down Expand Up @@ -263,4 +272,78 @@ export class ComboChartComponent extends BaseChart<ComboCanvasChartSpec> {
const measures = get(measuresStore);
return this.provider.getChartDomainValues(measures);
}

override getExploreTransformerProperties(): Partial<ExploreState> {
const spec = get(this.specStore);
const { dimensionFilters, dimensionThresholdFilters } = splitWhereFilter(
this.componentFilters,
);
const timeGrain = get(this.timeAndFilterStore)?.timeGrain;

const columns: PivotChipData[] = [];
const rows: PivotChipData[] = [];

if (spec.x?.field) {
if (spec.x.type === "temporal") {
rows.push({
id: timeGrain || V1TimeGrain.TIME_GRAIN_DAY,
title: spec.x.field,
type: PivotChipType.Time,
});
} else {
rows.push({
id: spec.x.field,
title: spec.x.field,
type: PivotChipType.Dimension,
});
}
}

if (spec.y1?.field && spec.y1.type === "quantitative") {
columns.push({
id: spec.y1.field,
title: spec.y1.field,
type: PivotChipType.Measure,
});
}

if (spec.y2?.field && spec.y2.type === "quantitative") {
columns.push({
id: spec.y2.field,
title: spec.y2.field,
type: PivotChipType.Measure,
});
}

const hasDimensionRows = rows.some(
(r) => r.type === PivotChipType.Dimension,
);
if (hasDimensionRows) {
const timeChips = rows.filter((r) => r.type === PivotChipType.Time);
const nonTimeRows = rows.filter((r) => r.type !== PivotChipType.Time);
columns.push(...timeChips);
rows.length = 0;
rows.push(...nonTimeRows);
}

const pivot: PivotState = {
columns,
rows,
expanded: {},
sorting: [],
columnPage: 0,
rowPage: 0,
enableComparison: false,
tableMode: "nest",
activeCell: null,
};

return {
whereFilter: dimensionFilters,
dimensionThresholdFilters,
showTimeComparison: false,
activePage: DashboardState_ActivePage.PIVOT,
pivot,
};
}
}
Loading
Loading