From cf4ff65afbf6e3f979b8554f043f3a5de68217ba Mon Sep 17 00:00:00 2001 From: Dhiraj Kumar Date: Wed, 1 Jul 2026 23:31:02 +0530 Subject: [PATCH 1/4] add config for default sort --- .../pivot/CanvasPivotDisplay.svelte | 10 +- .../canvas/components/pivot/default-sort.ts | 167 ++++++++++++++++++ .../features/canvas/components/pivot/index.ts | 42 ++++- .../canvas/inspector/DefaultSortInput.svelte | 87 +++++++++ .../canvas/inspector/ParamMapper.svelte | 5 + .../src/features/canvas/inspector/types.ts | 3 +- 6 files changed, 303 insertions(+), 11 deletions(-) create mode 100644 web-common/src/features/canvas/components/pivot/default-sort.ts create mode 100644 web-common/src/features/canvas/inspector/DefaultSortInput.svelte diff --git a/web-common/src/features/canvas/components/pivot/CanvasPivotDisplay.svelte b/web-common/src/features/canvas/components/pivot/CanvasPivotDisplay.svelte index 4b33042a9542..c11947c8dfa0 100644 --- a/web-common/src/features/canvas/components/pivot/CanvasPivotDisplay.svelte +++ b/web-common/src/features/canvas/components/pivot/CanvasPivotDisplay.svelte @@ -1,5 +1,6 @@ + +
+ + + {#if shownSort} +
+ {#each chips as chip, i (i)} + {#if i > 0} + + + + {/if} + + {chip.label} + + {/each} + + + +
+ +
+ + {#if storedDefault} + + {/if} +
+ {:else} + + Sort a column on the table to set it as the default. + + {/if} +
diff --git a/web-common/src/features/canvas/inspector/ParamMapper.svelte b/web-common/src/features/canvas/inspector/ParamMapper.svelte index 31a050177106..3890c4160509 100644 --- a/web-common/src/features/canvas/inspector/ParamMapper.svelte +++ b/web-common/src/features/canvas/inspector/ParamMapper.svelte @@ -17,6 +17,7 @@ import MetricsSQLInput from "./chart/MetricsSQLInput.svelte"; import PositionalFieldConfig from "./chart/PositionalFieldConfig.svelte"; import ComparisonInput from "./ComparisonInput.svelte"; + import DefaultSortInput from "./DefaultSortInput.svelte"; import MultiFieldInput from "./fields/MultiFieldInput.svelte"; import SingleFieldInput from "./fields/SingleFieldInput.svelte"; import LabelsInput from "./LabelsInput.svelte"; @@ -235,6 +236,10 @@ }} /> + + {:else if config.type === "default_sort" && component instanceof PivotCanvasComponent} + + {:else if config.type === "switcher_tab"} Date: Sun, 5 Jul 2026 12:34:12 +0530 Subject: [PATCH 2/4] handle time and async labels --- .../canvas/components/pivot/default-sort.ts | 59 ++++++++++++++++--- .../canvas/inspector/DefaultSortInput.svelte | 50 ++++++++++------ 2 files changed, 81 insertions(+), 28 deletions(-) diff --git a/web-common/src/features/canvas/components/pivot/default-sort.ts b/web-common/src/features/canvas/components/pivot/default-sort.ts index e9723d21080a..8135a1d8caa0 100644 --- a/web-common/src/features/canvas/components/pivot/default-sort.ts +++ b/web-common/src/features/canvas/components/pivot/default-sort.ts @@ -1,15 +1,17 @@ +import { + extractNumbers, + getTimeGrainFromDimension, + isTimeDimension, +} from "@rilldata/web-common/features/dashboards/pivot/pivot-utils"; import { COMPARISON_DELTA, COMPARISON_PERCENT, COMPARISON_VALUE, type PivotDataStoreConfig, } from "@rilldata/web-common/features/dashboards/pivot/types"; -import { - extractNumbers, - getTimeGrainFromDimension, - isTimeDimension, -} from "@rilldata/web-common/features/dashboards/pivot/pivot-utils"; import { TIME_GRAIN } from "@rilldata/web-common/lib/time/config"; +import { convertISOStringToJSDateWithSameTimeAsSelectedTimeZone } from "@rilldata/web-common/lib/time/timezone"; +import { timeFormat } from "d3-time-format"; import type { DefaultSort } from "./index"; /** Separator between column-dimension values in a nested-sort label. */ @@ -61,11 +63,13 @@ export function getSortChips( const chips: SortChip[] = parts.map((part) => { const { c, v } = extractNumbers(part); const dimensionName = config.colDimensionNames[c]; + const value = columnDimensionAxes[dimensionName]?.[v] ?? ""; + const isTime = isTimeDimension(dimensionName, config.time.timeDimension); return { - label: columnDimensionAxes[dimensionName]?.[v] ?? "", - type: isTimeDimension(dimensionName, config.time.timeDimension) - ? "time" - : "dimension", + label: isTime + ? formatColumnTimeValue(value, dimensionName, config) + : value, + type: isTime ? "time" : "dimension", }; }); @@ -84,6 +88,29 @@ export function getSortChips( ]; } +/** + * Whether all chip values for a sort id can be resolved from the currently + * loaded data. Nested pivot leaf accessors reference column-dimension values + * that arrive asynchronously; until they load, chips would render blank or, + * for time dimensions, as "NaN". Stable fields resolve synchronously. + */ +export function areSortChipsReady( + id: string, + config: PivotDataStoreConfig, + columnDimensionAxes: Record = {}, +): boolean { + if (!NESTED_ACCESSOR_RE.test(id)) return true; + + const [colPart] = id.split("m"); + const parts = colPart.split("_").filter(Boolean); + + return parts.every((part) => { + const { c, v } = extractNumbers(part); + const dimensionName = config.colDimensionNames[c]; + return columnDimensionAxes[dimensionName]?.[v] != null; + }); +} + /** * Resolves the field type of a sort id, used to color the inspector chip. * A nested pivot leaf accessor sorts by its measure, so it reads as "measure". @@ -105,6 +132,20 @@ const COMPARISON_MODIFIER: Record = { [COMPARISON_PERCENT]: " Δ %", }; +function formatColumnTimeValue( + value: string, + dimensionName: string, + config: PivotDataStoreConfig, +): string { + const grain = getTimeGrainFromDimension(dimensionName); + const dt = convertISOStringToJSDateWithSameTimeAsSelectedTimeZone( + value, + config.time.timeZone || "UTC", + ); + const formatter = timeFormat(grain ? TIME_GRAIN[grain].d3format : "%H:%M"); + return formatter(dt); +} + function getFieldLabel(field: string, config: PivotDataStoreConfig): string { if (isTimeDimension(field, config.time.timeDimension)) { const grain = getTimeGrainFromDimension(field); diff --git a/web-common/src/features/canvas/inspector/DefaultSortInput.svelte b/web-common/src/features/canvas/inspector/DefaultSortInput.svelte index 29731754c6cc..9790ea98d318 100644 --- a/web-common/src/features/canvas/inspector/DefaultSortInput.svelte +++ b/web-common/src/features/canvas/inspector/DefaultSortInput.svelte @@ -4,8 +4,10 @@ import InputLabel from "@rilldata/web-common/components/forms/InputLabel.svelte"; import ArrowDown from "@rilldata/web-common/components/icons/ArrowDown.svelte"; import ChevronRight from "@rilldata/web-common/components/icons/ChevronRight.svelte"; + import DelayedCircleOutlineSpinner from "@rilldata/web-common/components/spinner/DelayedCircleOutlineSpinner.svelte"; import type { PivotCanvasComponent } from "../components/pivot"; import { + areSortChipsReady, getSortChips, getSortLabel, sortEquals, @@ -27,6 +29,10 @@ // Show the active sort while the user is sorting; otherwise the stored default. $: shownSort = activeSort ?? storedDefault; + $: chipsReady = shownSort + ? areSortChipsReady(shownSort.id, $config, columnDimensionAxes) + : true; + $: chips = shownSort ? getSortChips(shownSort.id, $config, columnDimensionAxes) : []; @@ -51,25 +57,31 @@ {#if shownSort} -
- {#each chips as chip, i (i)} - {#if i > 0} - - - - {/if} - - {chip.label} - - {/each} - - - -
+ {#if chipsReady} +
+ {#each chips as chip, i (i)} + {#if i > 0} + + + + {/if} + + {chip.label} + + {/each} + + + +
+ {:else} +
+ +
+ {/if}
{:else}
- +
{/if}