{#if isNull}
no data
{:else}
diff --git a/web-common/src/features/dashboards/leaderboard/ContextColumnValue.svelte b/web-common/src/features/dashboards/leaderboard/ContextColumnValue.svelte
index 7e6ee866344..acaac9ec346 100644
--- a/web-common/src/features/dashboards/leaderboard/ContextColumnValue.svelte
+++ b/web-common/src/features/dashboards/leaderboard/ContextColumnValue.svelte
@@ -5,12 +5,14 @@
import type { LeaderboardItemData } from "./leaderboard-utils";
import { formatProperFractionAsPercent } from "@rilldata/web-common/lib/number-formatting/proper-fraction-formatter";
import { formatMeasurePercentageDifference } from "@rilldata/web-common/lib/number-formatting/percentage-formatter";
+ import { onDestroy, onMount } from "svelte";
export let itemData: LeaderboardItemData;
const {
selectors: {
contextColumn: {
+ contextColumn,
widthPx,
isDeltaAbsolute,
isDeltaPercent,
@@ -19,36 +21,59 @@
},
numberFormat: { activeMeasureFormatter },
},
+ actions: {
+ contextCol: { observeContextColumnWidth },
+ },
} = getStateManagers();
$: negativeChange = itemData.deltaAbs !== null && itemData.deltaAbs < 0;
$: noChangeData = itemData.deltaRel === null;
+
+ let element: HTMLElement;
+
+ $: {
+ // Re-observe the width when the context column changes,
+ // but after a short delay to allow the DOM to update.
+ if (element && $contextColumn) {
+ setTimeout(() => {
+ // the element may be gone by the time we get here,
+ // if so, don't try to observe it
+ if (!element) return;
+ observeContextColumnWidth(
+ $contextColumn,
+ element.getBoundingClientRect().width
+ );
+ }, 17);
+ }
+ }
{#if !$isHidden}
-
- {#if $isPercentOfTotal}
-
- {:else if noChangeData}
-
no data
- {:else if $isDeltaPercent}
-
- {:else if $isDeltaAbsolute}
-
- {/if}
+
+
+ {#if $isPercentOfTotal}
+
+ {:else if noChangeData}
+
no data
+ {:else if $isDeltaPercent}
+
+ {:else if $isDeltaAbsolute}
+
+ {/if}
+
{/if}
diff --git a/web-common/src/features/dashboards/leaderboard/LeaderboardContextColumnMenu.svelte b/web-common/src/features/dashboards/leaderboard/LeaderboardContextColumnMenu.svelte
index 823cbf5da67..62c821ddad9 100644
--- a/web-common/src/features/dashboards/leaderboard/LeaderboardContextColumnMenu.svelte
+++ b/web-common/src/features/dashboards/leaderboard/LeaderboardContextColumnMenu.svelte
@@ -7,11 +7,18 @@
import SelectMenu from "@rilldata/web-common/components/menu/compositions/SelectMenu.svelte";
import type { SelectMenuItem } from "@rilldata/web-common/components/menu/types";
- export let metricViewName: string;
+ // export let metricViewName: string;
export let validPercentOfTotal: boolean;
- let metricsExplorer: MetricsExplorerEntity;
- $: metricsExplorer = $metricsExplorerStore.entities[metricViewName];
+ const {
+ selectors: {
+ contextColumn: { contextColumn },
+ },
+ actions: {
+ contextCol: { setContextColumn },
+ },
+ } = getStateManagers();
+
const timeControlsStore = useTimeControlStore(getStateManagers());
const handleContextValueButtonGroupClick = (evt) => {
@@ -19,7 +26,7 @@
// CAST SAFETY: the value.key passed up from the evt must
// be a LeaderboardContextColumn
const key = value.key as LeaderboardContextColumn;
- metricsExplorerStore.setContextColumn(metricViewName, key);
+ setContextColumn(key);
};
let options: SelectMenuItem[];
@@ -47,7 +54,7 @@
// CAST SAFETY: the selection will always be one of the options
$: selection = options.find(
- (option) => option.key === metricsExplorer.leaderboardContextColumn
+ (option) => option.key === $contextColumn
) as SelectMenuItem;
diff --git a/web-common/src/features/dashboards/leaderboard/LeaderboardControls.svelte b/web-common/src/features/dashboards/leaderboard/LeaderboardControls.svelte
index 748b63d5561..70d1c308fef 100644
--- a/web-common/src/features/dashboards/leaderboard/LeaderboardControls.svelte
+++ b/web-common/src/features/dashboards/leaderboard/LeaderboardControls.svelte
@@ -13,9 +13,17 @@
import { metricsExplorerStore } from "web-common/src/features/dashboards/stores/dashboard-stores";
import { useMetaQuery } from "../selectors";
import LeaderboardContextColumnMenu from "./LeaderboardContextColumnMenu.svelte";
+ import { getStateManagers } from "../state-managers/state-managers";
export let metricViewName;
+ const {
+ actions: {
+ contextCol: { setContextColumn },
+ setLeaderboardMeasureName,
+ },
+ } = getStateManagers();
+
$: metaQuery = useMetaQuery($runtime.instanceId, metricViewName);
$: measures = $metaQuery.data?.measures;
@@ -24,10 +32,7 @@
$: metricsExplorer = $metricsExplorerStore.entities[metricViewName];
function handleMeasureUpdate(event: CustomEvent) {
- metricsExplorerStore.setLeaderboardMeasureName(
- metricViewName,
- event.detail.key
- );
+ setLeaderboardMeasureName(event.detail.key);
}
function measureKeyAndMain(measure: MetricsViewSpecMeasureV2) {
@@ -93,10 +98,7 @@
metricsExplorer?.leaderboardContextColumn ===
LeaderboardContextColumn.PERCENT
) {
- metricsExplorerStore.setContextColumn(
- metricViewName,
- LeaderboardContextColumn.HIDDEN
- );
+ setContextColumn(LeaderboardContextColumn.HIDDEN);
}
$: showHideDimensions = createShowHideDimensionsStore(
@@ -145,7 +147,7 @@
on:select={handleMeasureUpdate}
/>
-
+
{:else}
{
+ for (const contextColumn in contextColumnWidths) {
+ contextColumnWidths[contextColumn as LeaderboardContextColumn] =
+ contextColWidthDefaults[contextColumn as LeaderboardContextColumn];
+ }
+ console.log(
+ "resetAllContextColumnWidths",
+ contextColWidthDefaults,
+ contextColumnWidths
+ );
+};
+
+/**
+ * Observe this width value, updating the overall width of
+ * the context column if the given width is larger than the
+ * current width.
+ */
+export const observeContextColumnWidth = (
+ { dashboard }: DashboardMutables,
+ contextColumn: LeaderboardContextColumn,
+ width: number
+) => {
+ dashboard.contextColumnWidths[contextColumn] = Math.min(
+ Math.max(width, dashboard.contextColumnWidths[contextColumn]),
+ CONTEXT_COL_MAX_WIDTH
+ );
+};
+
export const contextColActions = {
/**
* Updates the dashboard to use the context column of the given type,
* as well as updating to sort by that context column.
*/
setContextColumn,
+
+ /**
+ * Observe this width value, updating the overall width of
+ * the context column if the given width is larger than the
+ * current width.
+ */
+ observeContextColumnWidth,
};
diff --git a/web-common/src/features/dashboards/state-managers/actions/core-actions.ts b/web-common/src/features/dashboards/state-managers/actions/core-actions.ts
index cc57838ddac..acf421281b5 100644
--- a/web-common/src/features/dashboards/state-managers/actions/core-actions.ts
+++ b/web-common/src/features/dashboards/state-managers/actions/core-actions.ts
@@ -1,3 +1,4 @@
+import { resetAllContextColumnWidths } from "./context-columns";
import type { DashboardMutables } from "./types";
export const setLeaderboardMeasureName = (
@@ -5,4 +6,7 @@ export const setLeaderboardMeasureName = (
name: string
) => {
dashboard.leaderboardMeasureName = name;
+
+ // reset column widths when changing the leaderboard measure
+ resetAllContextColumnWidths(dashboard.contextColumnWidths);
};
diff --git a/web-common/src/features/dashboards/state-managers/selectors/context-column.ts b/web-common/src/features/dashboards/state-managers/selectors/context-column.ts
index 5326760dc9e..407399478c7 100644
--- a/web-common/src/features/dashboards/state-managers/selectors/context-column.ts
+++ b/web-common/src/features/dashboards/state-managers/selectors/context-column.ts
@@ -1,18 +1,14 @@
import { LeaderboardContextColumn } from "../../leaderboard-context-column";
+import { contextColWidthDefaults } from "../../stores/metrics-explorer-entity";
import type { DashboardDataSources } from "./types";
-const contextColumnWidth = (contextType: LeaderboardContextColumn): string => {
- switch (contextType) {
- case LeaderboardContextColumn.DELTA_ABSOLUTE:
- case LeaderboardContextColumn.DELTA_PERCENT:
- return "56px";
- case LeaderboardContextColumn.PERCENT:
- return "44px";
- case LeaderboardContextColumn.HIDDEN:
- return "0px";
- default:
- throw new Error("Invalid context column, all cases must be handled");
+const contextColumnWidth = ({ dashboard }: DashboardDataSources): string => {
+ const contextType = dashboard.leaderboardContextColumn;
+ const width = dashboard.contextColumnWidths[contextType];
+ if (typeof width === "number") {
+ return width + "px";
}
+ return "0px";
};
export const contextColSelectors = {
@@ -61,6 +57,5 @@ export const contextColSelectors = {
* returns a css style string specifying the width of the context
* column in the leaderboards.
*/
- widthPx: ({ dashboard }: DashboardDataSources) =>
- contextColumnWidth(dashboard.leaderboardContextColumn),
+ widthPx: contextColumnWidth,
};
diff --git a/web-common/src/features/dashboards/stores/dashboard-store-defaults.ts b/web-common/src/features/dashboards/stores/dashboard-store-defaults.ts
index 8dbdcbb251c..adf0ba5dd26 100644
--- a/web-common/src/features/dashboards/stores/dashboard-store-defaults.ts
+++ b/web-common/src/features/dashboards/stores/dashboard-store-defaults.ts
@@ -3,7 +3,10 @@ import {
SortDirection,
SortType,
} from "@rilldata/web-common/features/dashboards/proto-state/derived-types";
-import type { MetricsExplorerEntity } from "@rilldata/web-common/features/dashboards/stores/metrics-explorer-entity";
+import {
+ contextColWidthDefaults,
+ type MetricsExplorerEntity,
+} from "@rilldata/web-common/features/dashboards/stores/metrics-explorer-entity";
import { getLocalUserPreferences } from "@rilldata/web-common/features/dashboards/user-preferences";
import { getTimeComparisonParametersForComponent } from "@rilldata/web-common/lib/time/comparisons";
import { DEFAULT_TIME_RANGES } from "@rilldata/web-common/lib/time/config";
@@ -106,20 +109,27 @@ export function getDefaultMetricsExplorerEntity(
name: string,
metricsView: V1MetricsViewSpec,
fullTimeRange: V1ColumnTimeRangeResponse | undefined
-) {
+): MetricsExplorerEntity {
+ // CAST SAFETY: safe b/c (1) measure.name is a string if defined,
+ // and (2) we filter out undefined values
+ const defaultMeasureNames = (metricsView?.measures
+ ?.map((measure) => measure?.name)
+ .filter((name) => name !== undefined) ?? []) as string[];
+
+ // CAST SAFETY: safe b/c (1) measure.name is a string if defined,
+ // and (2) we filter out undefined values
+ const defaultDimNames = (metricsView?.dimensions
+ ?.map((dim) => dim.name)
+ .filter((name) => name !== undefined) ?? []) as string[];
+
const metricsExplorer: MetricsExplorerEntity = {
name,
- selectedMeasureNames: metricsView.measures.map((measure) => measure.name),
-
- visibleMeasureKeys: new Set(
- metricsView.measures.map((measure) => measure.name)
- ),
+ selectedMeasureNames: [...defaultMeasureNames],
+ visibleMeasureKeys: new Set(...defaultMeasureNames),
allMeasuresVisible: true,
- visibleDimensionKeys: new Set(
- metricsView.dimensions.map((dim) => dim.name)
- ),
+ visibleDimensionKeys: new Set(...defaultDimNames),
allDimensionsVisible: true,
- leaderboardMeasureName: metricsView.measures[0]?.name,
+ leaderboardMeasureName: defaultMeasureNames[0],
filters: {
include: [],
exclude: [],
@@ -132,6 +142,7 @@ export function getDefaultMetricsExplorerEntity(
showTimeComparison: false,
dimensionSearchText: "",
pinIndex: -1,
+ contextColumnWidths: { ...contextColWidthDefaults },
};
// set time range related stuff
setDefaultTimeRange(metricsView, metricsExplorer, fullTimeRange);
diff --git a/web-common/src/features/dashboards/stores/dashboard-stores.ts b/web-common/src/features/dashboards/stores/dashboard-stores.ts
index 1822b858a4f..4d5a06761ef 100644
--- a/web-common/src/features/dashboards/stores/dashboard-stores.ts
+++ b/web-common/src/features/dashboards/stores/dashboard-stores.ts
@@ -210,7 +210,15 @@ const metricViewReducers = {
});
},
+ /**
+ * DEPRECATED!!!
+ * use setLeaderboardMeasureName via:
+ * getStateManagers().actions.setLeaderboardMeasureName
+ */
setLeaderboardMeasureName(name: string, measureName: string) {
+ console.warn(
+ "setLeaderboardMeasureName is deprecated. Use setLeaderboardMeasureName via `getStateManagers().actions.setLeaderboardMeasureName`"
+ );
updateMetricsExplorerByName(name, (metricsExplorer) => {
metricsExplorer.leaderboardMeasureName = measureName;
});
diff --git a/web-common/src/features/dashboards/stores/metrics-explorer-entity.ts b/web-common/src/features/dashboards/stores/metrics-explorer-entity.ts
index db82cca3d63..7f8f4342b1a 100644
--- a/web-common/src/features/dashboards/stores/metrics-explorer-entity.ts
+++ b/web-common/src/features/dashboards/stores/metrics-explorer-entity.ts
@@ -1,4 +1,4 @@
-import type { LeaderboardContextColumn } from "@rilldata/web-common/features/dashboards/leaderboard-context-column";
+import { LeaderboardContextColumn } from "@rilldata/web-common/features/dashboards/leaderboard-context-column";
import type {
SortDirection,
SortType,
@@ -129,6 +129,13 @@ export interface MetricsExplorerEntity {
*/
leaderboardContextColumn: LeaderboardContextColumn;
+ /**
+ * Width of each context column. Needs to be reset to default
+ * when changing context column or switching between leaderboard
+ * and dimension detail table
+ */
+ contextColumnWidths: ContextColWidths;
+
/**
* The name of the dimension that is currently shown in the dimension
* detail table. If this is undefined, then the dimension detail table
@@ -138,3 +145,15 @@ export interface MetricsExplorerEntity {
proto?: string;
}
+
+export type ContextColWidths = {
+ [LeaderboardContextColumn.DELTA_ABSOLUTE]: number;
+ [LeaderboardContextColumn.DELTA_PERCENT]: number;
+ [LeaderboardContextColumn.PERCENT]: number;
+};
+
+export const contextColWidthDefaults: ContextColWidths = {
+ [LeaderboardContextColumn.DELTA_ABSOLUTE]: 56,
+ [LeaderboardContextColumn.DELTA_PERCENT]: 44,
+ [LeaderboardContextColumn.PERCENT]: 44,
+};