diff --git a/web-common/src/features/dashboards/big-number/BigNumberTooltipContent.svelte b/web-common/src/features/dashboards/big-number/BigNumberTooltipContent.svelte index 9c7ca2d4933..58659a8e8cf 100644 --- a/web-common/src/features/dashboards/big-number/BigNumberTooltipContent.svelte +++ b/web-common/src/features/dashboards/big-number/BigNumberTooltipContent.svelte @@ -3,6 +3,9 @@ import TooltipDescription from "@rilldata/web-common/components/tooltip/TooltipDescription.svelte"; import TooltipTitle from "@rilldata/web-common/components/tooltip/TooltipTitle.svelte"; import type { MetricsViewSpecMeasure } from "@rilldata/web-common/runtime-client"; + import TooltipShortcutContainer from "@rilldata/web-common/components/tooltip/TooltipShortcutContainer.svelte"; + import StackingWord from "@rilldata/web-common/components/tooltip/StackingWord.svelte"; + import Shortcut from "@rilldata/web-common/components/tooltip/Shortcut.svelte"; export let measure: MetricsViewSpecMeasure; export let value = ""; @@ -25,4 +28,13 @@ {description} + + +
+ Copy to clipboard +
+ + + Click + +
diff --git a/web-common/src/features/dashboards/leaderboard/Leaderboard.svelte b/web-common/src/features/dashboards/leaderboard/Leaderboard.svelte index 544708905b5..58a73652226 100644 --- a/web-common/src/features/dashboards/leaderboard/Leaderboard.svelte +++ b/web-common/src/features/dashboards/leaderboard/Leaderboard.svelte @@ -378,6 +378,7 @@ {filterExcludeMode} {atLeastOneActive} {dimensionName} + dataType={dimension.dataType?.code ?? ""} {itemData} {isValidPercentOfTotal} {leaderboardShowContextForAllMeasures} @@ -396,6 +397,7 @@ + import { onDestroy } from "svelte"; + import * as Tooltip from "@rilldata/web-common/components/tooltip-v2"; + import Shortcut from "@rilldata/web-common/components/tooltip/Shortcut.svelte"; + import StackingWord from "@rilldata/web-common/components/tooltip/StackingWord.svelte"; + import { + copyToClipboard, + isClipboardApiSupported, + } from "@rilldata/web-common/lib/actions/copy-to-clipboard.ts"; + import { builderActions, getAttrs } from "bits-ui"; + import { TOOLTIP_STRING_LIMIT } from "@rilldata/web-common/layout/config.ts"; + import { modified } from "@rilldata/web-common/lib/actions/modified-click.ts"; + import { cellInspectorStore } from "@rilldata/web-common/features/dashboards/stores/cell-inspector-store.ts"; + import { FormattedDataType } from "@rilldata/web-common/components/data-types"; + + export let value: string; + export let dataType: string; + export let cellType: "dimension" | "measure" | "comparison"; + export let className: string = ""; + export let background: string = ""; + + const HideLeaderboardTooltipAfter = 3000; + + const clipboardSupported = + typeof navigator !== "undefined" ? isClipboardApiSupported() : false; + const disabled = !clipboardSupported; + + let tooltipActive = false; + $: if (tooltipActive) { + showTemporarily(); + } else { + clearHideTimer(); + } + + let hideTimer: ReturnType | undefined; + + function clearHideTimer() { + if (hideTimer) { + clearTimeout(hideTimer); + hideTimer = undefined; + } + } + + function showTemporarily() { + if (disabled) return; + clearHideTimer(); + hideTimer = setTimeout(() => { + tooltipActive = false; + }, HideLeaderboardTooltipAfter); + } + + function shiftClickHandler(label: string) { + let truncatedLabel = label?.toString(); + if (truncatedLabel?.length > TOOLTIP_STRING_LIMIT) { + truncatedLabel = `${truncatedLabel.slice(0, TOOLTIP_STRING_LIMIT)}...`; + } + copyToClipboard( + label, + `copied dimension value "${truncatedLabel}" to clipboard`, + ); + } + + onDestroy(clearHideTimer); + + + + + shiftClickHandler(value), + })} + on:pointerover={() => { + if (value?.toString) { + // Always update the value in the store, but don't change visibility + cellInspectorStore.updateValue(value.toString()); + } + }} + on:focus={() => { + if (value?.toString) { + // Always update the value in the store, but don't change visibility + cellInspectorStore.updateValue(value.toString()); + } + }} + on:mouseleave={() => (tooltipActive = false)} + style:background + class="{cellType}-cell {className}" + > + + + + + {#if clipboardSupported && !disabled} + + +
+
+ Copy + this value to clipboard +
+ + + Click + +
+
+ {/if} +
+ + diff --git a/web-common/src/features/dashboards/leaderboard/LeaderboardRow.svelte b/web-common/src/features/dashboards/leaderboard/LeaderboardRow.svelte index e71970d7bf4..9d67f579ef9 100644 --- a/web-common/src/features/dashboards/leaderboard/LeaderboardRow.svelte +++ b/web-common/src/features/dashboards/leaderboard/LeaderboardRow.svelte @@ -2,14 +2,10 @@ import FormattedDataType from "@rilldata/web-common/components/data-types/FormattedDataType.svelte"; import PercentageChange from "@rilldata/web-common/components/data-types/PercentageChange.svelte"; import ExternalLink from "@rilldata/web-common/components/icons/ExternalLink.svelte"; - import { TOOLTIP_STRING_LIMIT } from "@rilldata/web-common/layout/config"; - import { copyToClipboard } from "@rilldata/web-common/lib/actions/copy-to-clipboard"; - import { modified } from "@rilldata/web-common/lib/actions/modified-click"; import { clamp } from "@rilldata/web-common/lib/clamp"; import { formatMeasurePercentageDifference } from "@rilldata/web-common/lib/number-formatting/percentage-formatter"; import { slide } from "svelte/transition"; import { type LeaderboardItemData, makeHref } from "./leaderboard-utils"; - import { cellInspectorStore } from "../stores/cell-inspector-store"; import LeaderboardItemFilterIcon from "./LeaderboardItemFilterIcon.svelte"; import LongBarZigZag from "./LongBarZigZag.svelte"; import { @@ -19,9 +15,11 @@ deltaColumn, MEASURES_PADDING, } from "./leaderboard-widths"; + import LeaderboardCell from "@rilldata/web-common/features/dashboards/leaderboard/LeaderboardCell.svelte"; export let itemData: LeaderboardItemData; export let dimensionName: string; + export let dataType: string; export let borderTop = false; export let borderBottom = false; export let isBeingCompared: boolean; @@ -160,16 +158,9 @@ }), ); - function shiftClickHandler(label: string) { - let truncatedLabel = label?.toString(); - if (truncatedLabel?.length > TOOLTIP_STRING_LIMIT) { - truncatedLabel = `${truncatedLabel.slice(0, TOOLTIP_STRING_LIMIT)}...`; - } - copyToClipboard( - label, - `copied dimension value "${truncatedLabel}" to clipboard`, - ); - } + $: dimensionCellClass = `relative size-full flex flex-none justify-between items-center leaderboard-label ${ + atLeastOneActive ? "cursor-pointer" : "" + } ${excluded ? "ui-copy-disabled" : ""} ${!excluded && selected ? "ui-copy-strong" : ""}`; function onDimensionCellClick(e: MouseEvent) { // Check if user has selected text @@ -211,30 +202,12 @@ selectionIndex={itemData?.selectedIndex} /> - shiftClickHandler(dimensionValue), - })} - on:pointerover={() => { - if (dimensionValue) { - // Always update the value in the store, but don't change visibility - cellInspectorStore.updateValue(dimensionValue.toString()); - } - }} - on:focus={() => { - if (dimensionValue) { - // Always update the value in the store, but don't change visibility - cellInspectorStore.updateValue(dimensionValue.toString()); - } - }} - class="relative size-full flex flex-none justify-between items-center leaderboard-label" - style:background={dimensionGradients} + @@ -263,31 +236,16 @@ {/if} - + {#each Object.keys(values) as measureName} - shiftClickHandler(values[measureName]?.toString() || ""), - })} - style:background={leaderboardMeasureNames.length === 1 + { - const value = values[measureName]?.toString() || ""; - if (value) { - cellInspectorStore.updateValue(value); - } - }} - on:focus={() => { - const value = values[measureName]?.toString() || ""; - if (value) { - cellInspectorStore.updateValue(value); - } - }} >
{/if} - + {#if isValidPercentOfTotal(measureName) && shouldShowContextColumns(measureName)} - - shiftClickHandler(pctOfTotals[measureName]?.toString() || ""), - })} - on:pointerover={() => { - const value = pctOfTotals[measureName]?.toString() || ""; - if (value) { - cellInspectorStore.updateValue(value); - } - }} - on:focus={() => { - const value = pctOfTotals[measureName]?.toString() || ""; - if (value) { - cellInspectorStore.updateValue(value); - } - }} + {/if} - + {/if} {#if isTimeComparisonActive && shouldShowContextColumns(measureName)} - - shiftClickHandler(deltaAbsMap[measureName]?.toString() || ""), - })} - on:pointerover={() => { - const value = deltaAbsMap[measureName]?.toString() || ""; - if (value) { - cellInspectorStore.updateValue(value); - } - }} - on:focus={() => { - const value = deltaAbsMap[measureName]?.toString() || ""; - if (value) { - cellInspectorStore.updateValue(value); - } - }} + - + {/if} {#if isTimeComparisonActive && shouldShowContextColumns(measureName)} - - shiftClickHandler(deltaRels[measureName]?.toString() || ""), - })} - on:pointerover={() => { - const value = deltaRels[measureName]?.toString() || ""; - if (value) { - cellInspectorStore.updateValue(value); - } - }} - on:focus={() => { - const value = deltaRels[measureName]?.toString() || ""; - if (value) { - cellInspectorStore.updateValue(value); - } - }} + {/if} - + {/if} {/each}