From 349c759743229d0125358706c831bb0b2a8f25a0 Mon Sep 17 00:00:00 2001 From: Brian Holmes <120223836+briangregoryholmes@users.noreply.github.com> Date: Wed, 11 Dec 2024 05:00:39 -0500 Subject: [PATCH] fix: leaderboard column percent of total width (#6243) --- .../dashboards/leaderboard/Leaderboard.svelte | 52 ++----- .../leaderboard/LeaderboardDisplay.svelte | 51 +++---- .../leaderboard/LeaderboardRow.svelte | 136 +++++++++--------- .../leaderboard/leaderboard-widths.ts | 120 +++------------- 4 files changed, 127 insertions(+), 232 deletions(-) diff --git a/web-common/src/features/dashboards/leaderboard/Leaderboard.svelte b/web-common/src/features/dashboards/leaderboard/Leaderboard.svelte index c917e9740fe..75d440e4874 100644 --- a/web-common/src/features/dashboards/leaderboard/Leaderboard.svelte +++ b/web-common/src/features/dashboards/leaderboard/Leaderboard.svelte @@ -18,7 +18,6 @@ cleanUpComparisonValue, compareLeaderboardValues, getSort, - type LeaderboardItemData, prepareLeaderboardItemData, } from "./leaderboard-utils"; import type { DimensionThresholdFilter } from "../stores/metrics-explorer-entity"; @@ -39,13 +38,14 @@ getFiltersForOtherDimensions, } from "../selectors"; import { getIndependentMeasures } from "../state-managers/selectors/measures"; - import { - LEADERBOARD_DEFAULT_COLUMN_WIDTHS, - type ColumnWidths, - } from "./leaderboard-widths"; import LeaderboardHeader from "./LeaderboardHeader.svelte"; import LeaderboardRow from "./LeaderboardRow.svelte"; import LoadingRows from "./LoadingRows.svelte"; + import { + valueColumn, + deltaColumn, + DEFAULT_COL_WIDTH, + } from "./leaderboard-widths"; const slice = 7; const gutterWidth = 24; @@ -62,20 +62,16 @@ export let metricsViewName: string; export let metricsView: V1MetricsViewSpec; export let sortType: SortType; + export let tableWidth: number; export let sortedAscending: boolean; export let isValidPercentOfTotal: boolean; export let timeControlsReady: boolean; + export let firstColumnWidth: number; export let isSummableMeasure: boolean; export let filterExcludeMode: boolean; export let atLeastOneActive: boolean; export let isBeingCompared: boolean; export let parentElement: HTMLElement; - export let columnWidths: ColumnWidths = LEADERBOARD_DEFAULT_COLUMN_WIDTHS; - export let estimateAndUpdateLeaderboardWidths: ( - dimensionName: string, - aboveTheFold: LeaderboardItemData[], - belowTheFold: LeaderboardItemData[], - ) => void; export let toggleDimensionValueSelection: ( dimensionName: string, dimensionValue: string, @@ -260,24 +256,6 @@ ); $: columnCount = comparisonTimeRange ? 3 : isValidPercentOfTotal ? 2 : 1; - - // Estimate the common column widths for all leaderboards - $: if (aboveTheFold.length || belowTheFoldRows.length) { - estimateAndUpdateLeaderboardWidths( - dimensionName, - aboveTheFold, - belowTheFoldRows, - ); - } - - $: tableWidth = - columnWidths.dimension + - columnWidths.value + - (comparisonTimeRange - ? columnWidths.delta + columnWidths.deltaPercent - : isValidPercentOfTotal - ? columnWidths.percentOfTotal - : 0);
- - + + {#if !!comparisonTimeRange} - - + + {:else if isValidPercentOfTotal} - + {/if} @@ -324,6 +302,7 @@ {#each aboveTheFold as itemData (itemData.dimensionValue)} @@ -343,6 +320,7 @@ {#each belowTheFoldRows as itemData, i (itemData.dimensionValue)} - import type { LeaderboardItemData } from "@rilldata/web-common/features/dashboards/leaderboard/leaderboard-utils"; import { getStateManagers } from "@rilldata/web-common/features/dashboards/state-managers/state-managers"; import type { V1Expression, @@ -11,9 +10,9 @@ import Leaderboard from "./Leaderboard.svelte"; import LeaderboardControls from "./LeaderboardControls.svelte"; import { - calculateAndUpdateAllLeaderboardWidths, - columnWidths, - resetColumnWidths, + DEFAULT_COL_WIDTH, + deltaColumn, + valueColumn, } from "./leaderboard-widths"; export let metricsViewName: string; @@ -50,31 +49,25 @@ let parentElement: HTMLDivElement; - function estimateAndUpdateLeaderboardWidths( - dimensionName: string, - aboveTheFold: LeaderboardItemData[], - selectedBelowTheFold: LeaderboardItemData[], - ) { - const firstColumnWidth = - !comparisonTimeRange && !$isValidPercentOfTotal ? 240 : 164; - calculateAndUpdateAllLeaderboardWidths( - dimensionName, - firstColumnWidth, - aboveTheFold, - selectedBelowTheFold, - $activeMeasureFormatter, - ); - } + $: ({ instanceId } = $runtime); - // Reset column widths when relevant data changes - $: { - activeMeasureName; - !!comparisonTimeRange; - !!timeRange; - $isValidPercentOfTotal; - resetColumnWidths(); + // Reset column widths when the measure changes + $: if (activeMeasureName) { + valueColumn.reset(); + deltaColumn.reset(); } - $: ({ instanceId } = $runtime); + + $: firstColumnWidth = + !comparisonTimeRange && !$isValidPercentOfTotal ? 240 : 164; + + $: tableWidth = + firstColumnWidth + + $valueColumn + + (comparisonTimeRange + ? $deltaColumn + DEFAULT_COL_WIDTH + : $isValidPercentOfTotal + ? DEFAULT_COL_WIDTH + : 0);
@@ -93,7 +86,9 @@ {whereFilter} {dimensionThresholdFilters} {instanceId} + {tableWidth} {timeRange} + {firstColumnWidth} sortedAscending={$sortedAscending} sortType={$sortType} filterExcludeMode={$isFilterExcludeMode(dimension.name)} @@ -106,8 +101,6 @@ {timeControlsReady} selectedValues={$selectedDimensionValues(dimension.name)} isBeingCompared={$isBeingComparedReadable(dimension.name)} - columnWidths={$columnWidths} - {estimateAndUpdateLeaderboardWidths} formatter={$activeMeasureFormatter} {setPrimaryDimension} {toggleSort} diff --git a/web-common/src/features/dashboards/leaderboard/LeaderboardRow.svelte b/web-common/src/features/dashboards/leaderboard/LeaderboardRow.svelte index c8003e96ad9..9adccae0a71 100644 --- a/web-common/src/features/dashboards/leaderboard/LeaderboardRow.svelte +++ b/web-common/src/features/dashboards/leaderboard/LeaderboardRow.svelte @@ -13,18 +13,15 @@ import LeaderboardItemFilterIcon from "./LeaderboardItemFilterIcon.svelte"; import LeaderboardTooltipContent from "./LeaderboardTooltipContent.svelte"; import LongBarZigZag from "./LongBarZigZag.svelte"; + import { + DEFAULT_COL_WIDTH, + deltaColumn, + valueColumn, + } from "./leaderboard-widths"; export let itemData: LeaderboardItemData; export let dimensionName: string; export let tableWidth: number; - export let columnWidths: { - dimension: number; - value: number; - percentOfTotal: number; - delta: number; - deltaPercent: number; - }; - export let gutterWidth: number; export let borderTop = false; export let borderBottom = false; export let isSummableMeasure: boolean; @@ -42,19 +39,37 @@ export let formatter: | ((_value: number | undefined) => undefined) | ((value: string | number) => string); + export let firstColumnWidth: number; let hovered = false; + let valueRect = new DOMRect(0, 0, DEFAULT_COL_WIDTH); + let deltaRect = new DOMRect(0, 0, DEFAULT_COL_WIDTH); $: ({ - dimensionValue: label, + dimensionValue, selectedIndex, pctOfTotal, - value: measureValue, - prevValue: comparisonValue, + value, + prevValue, + deltaAbs, + deltaRel, + uri, } = itemData); $: selected = selectedIndex >= 0; + $: formattedValue = value ? formatter(value) : null; + $: formattedDeltaRel = deltaRel + ? formatMeasurePercentageDifference(deltaRel) + : null; + $: formattedDelta = deltaAbs ? formatter(deltaAbs) : null; + + $: deltaElementWidth = deltaRect.width; + $: valueElementWith = valueRect.width; + + $: valueColumn.update(valueElementWith); + $: deltaColumn.update(deltaElementWidth); + // Super important special case: if there is not at least one "active" (selected) value, // we need to set *all* items to be included, because by default if a user has not // selected any values, we assume they want all values included in all calculations. @@ -63,19 +78,18 @@ : false; $: previousValueString = - comparisonValue !== undefined && comparisonValue !== null - ? formatter(comparisonValue) + prevValue !== undefined && prevValue !== null + ? formatter(prevValue) : undefined; - $: formattedValue = measureValue ? formatter(measureValue) : null; + $: negativeChange = deltaAbs !== null && deltaAbs < 0; - $: negativeChange = itemData.deltaAbs !== null && itemData.deltaAbs < 0; - - $: href = makeHref(itemData.uri); + $: href = makeHref(uri); $: percentOfTotal = isSummableMeasure && pctOfTotal ? pctOfTotal : 0; - $: barLength = (tableWidth - gutterWidth) * percentOfTotal; + $: barLength = tableWidth * percentOfTotal; + $: showZigZag = barLength > tableWidth; $: barColor = excluded ? "rgb(243 244 246)" @@ -83,32 +97,17 @@ ? "var(--color-primary-200)" : "var(--color-primary-100)"; - $: secondCellBarLength = clamp( - 0, - barLength - columnWidths.dimension, - columnWidths.value, - ); + $: secondCellBarLength = clamp(0, barLength - firstColumnWidth, $valueColumn); $: thirdCellBarLength = isTimeComparisonActive - ? clamp( - 0, - barLength - columnWidths.dimension - columnWidths.value, - columnWidths.delta, - ) + ? clamp(0, barLength - firstColumnWidth - $valueColumn, $deltaColumn) : isValidPercentOfTotal - ? clamp( - 0, - barLength - columnWidths.dimension - columnWidths.value, - columnWidths.percentOfTotal, - ) + ? clamp(0, barLength - firstColumnWidth - $valueColumn, DEFAULT_COL_WIDTH) : 0; $: fourthCellBarLength = isTimeComparisonActive ? clamp( 0, - barLength - - columnWidths.dimension - - columnWidths.value - - columnWidths.delta, - columnWidths.deltaPercent, + barLength - firstColumnWidth - $valueColumn - $deltaColumn, + DEFAULT_COL_WIDTH, ) : 0; @@ -131,8 +130,6 @@ ${fourthCellBarLength}px, transparent ${fourthCellBarLength}px)` : undefined; - $: showZigZag = barLength > tableWidth - gutterWidth; - // uri template or "true" string literal or undefined function makeHref(uriTemplateOrBoolean: string | boolean | null) { if (!uriTemplateOrBoolean) { @@ -141,7 +138,7 @@ const uri = uriTemplateOrBoolean === true - ? label + ? dimensionValue : uriTemplateOrBoolean.replace(/\s/g, ""); const hasProtocol = /^[a-zA-Z][a-zA-Z\d+\-.]*:/.test(uri); @@ -171,15 +168,16 @@ on:mouseenter={() => (hovered = true)} on:mouseleave={() => (hovered = false)} on:click={modified({ - shift: () => shiftClickHandler(label), + shift: () => shiftClickHandler(dimensionValue), click: (e) => toggleDimensionValueSelection( dimensionName, - label, + dimensionValue, false, e.ctrlKey || e.metaKey, ), })} + class="relative" > - + {#if previousValueString && hovered} + - +
+ +
+ {#if showZigZag && !isTimeComparisonActive && !isValidPercentOfTotal} {/if} - {#if isTimeComparisonActive} + + {#if isTimeComparisonActive || isValidPercentOfTotal} - - - - - {#if showZigZag} - + {#if isTimeComparisonActive} +
+ +
+ {:else} + + {#if showZigZag} + + {/if} {/if} - {:else if isValidPercentOfTotal} - - + {/if} + + {#if isTimeComparisonActive} + + {#if showZigZag} {/if} @@ -266,7 +270,7 @@