diff --git a/web-common/src/components/button/Button.svelte b/web-common/src/components/button/Button.svelte index 1971619c296..caf45918bbd 100644 --- a/web-common/src/components/button/Button.svelte +++ b/web-common/src/components/button/Button.svelte @@ -235,7 +235,7 @@ /* LINK STYLES */ .link { - @apply text-primary-600; + @apply text-primary-600 p-0; } .link:hover { diff --git a/web-common/src/components/menu/DashboardMetricsDraggableList.svelte b/web-common/src/components/menu/DashboardMetricsDraggableList.svelte new file mode 100644 index 00000000000..e5f03eaa219 --- /dev/null +++ b/web-common/src/components/menu/DashboardMetricsDraggableList.svelte @@ -0,0 +1,461 @@ + + + + + + + + + + + + diff --git a/web-common/src/components/menu/LeaderboardMeasureCountSelector.svelte b/web-common/src/components/menu/LeaderboardMeasureCountSelector.svelte index 7e35a4ace73..107ed34c229 100644 --- a/web-common/src/components/menu/LeaderboardMeasureCountSelector.svelte +++ b/web-common/src/components/menu/LeaderboardMeasureCountSelector.svelte @@ -7,9 +7,7 @@ export let measures: MetricsViewSpecMeasureV2[]; export let count: number = 1; - export let sortByMeasure: string | null; export let onMeasureCountChange: (count: number) => void; - export let resetSort: () => void; let isHovered = false; @@ -27,25 +25,8 @@ } } - function getFilteredMeasuresByMeasureCount( - measures: MetricsViewSpecMeasureV2[], - count: number, - ) { - return measures.slice(0, count); - } - - $: filteredMeasures = getFilteredMeasuresByMeasureCount(measures, count); $: visibleMeasuresCount = measures.length; - // Workaround for feature flag `leaderboardMeasureCount` - // If the sortByMeasure isn't in the filtered measures, we need to reset the sort - $: if ( - sortByMeasure && - !filteredMeasures.some((measure) => measure.name === sortByMeasure) - ) { - resetSort(); - } - // Update count to match visible measures when filtered count is greater $: if (visibleMeasuresCount < count) { onMeasureCountChange(visibleMeasuresCount); diff --git a/web-common/src/components/popover/popover-content.svelte b/web-common/src/components/popover/popover-content.svelte index f5cda74e931..9d1fe4c0b2e 100644 --- a/web-common/src/components/popover/popover-content.svelte +++ b/web-common/src/components/popover/popover-content.svelte @@ -2,13 +2,26 @@ import { cn, flyAndScale } from "@rilldata/web-common/lib/shadcn"; import { Popover as PopoverPrimitive } from "bits-ui"; - type $$Props = PopoverPrimitive.ContentProps; + type $$Props = PopoverPrimitive.ContentProps & { + overflowY?: string; + overflowX?: string; + minHeight?: string; + }; let className: $$Props["class"] = undefined; + export let transition: $$Props["transition"] = flyAndScale; export let transitionConfig: $$Props["transitionConfig"] = undefined; export let align: $$Props["align"] = "center"; export let sideOffset: $$Props["sideOffset"] = 4; + export let avoidCollisions: $$Props["avoidCollisions"] = true; + export let collisionPadding: $$Props["collisionPadding"] = 8; + export let collisionBoundary: $$Props["collisionBoundary"] = undefined; + export let fitViewport: $$Props["fitViewport"] = false; + export let strategy: $$Props["strategy"] = undefined; + export let overflowY: string = "auto"; + export let overflowX: string = "auto"; + export let minHeight: string = "0px"; export { className as class }; @@ -17,9 +30,17 @@ {transitionConfig} {align} {sideOffset} + {avoidCollisions} + {collisionPadding} + {collisionBoundary} + {fitViewport} + {strategy} {...$$restProps} class={cn( "z-50 w-72 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none", + overflowY && `overflow-y-${overflowY}`, + overflowX && `overflow-x-${overflowX}`, + minHeight && `min-h-[${minHeight}]`, className, )} > diff --git a/web-common/src/features/dashboards/leaderboard/LeaderboardControls.svelte b/web-common/src/features/dashboards/leaderboard/LeaderboardControls.svelte index 75422046c08..301da73f97d 100644 --- a/web-common/src/features/dashboards/leaderboard/LeaderboardControls.svelte +++ b/web-common/src/features/dashboards/leaderboard/LeaderboardControls.svelte @@ -5,9 +5,9 @@ import { metricsExplorerStore } from "web-common/src/features/dashboards/stores/dashboard-stores"; import { getStateManagers } from "../state-managers/state-managers"; import LeaderboardMeasureCountSelector from "@rilldata/web-common/components/menu/LeaderboardMeasureCountSelector.svelte"; - import { featureFlags } from "../../feature-flags"; import LeaderboardActiveMeasureDropdown from "@rilldata/web-common/components/menu/LeaderboardActiveMeasureDropdown.svelte"; - import { SortType } from "../proto-state/derived-types"; + import { featureFlags } from "@rilldata/web-common/features/feature-flags"; + import DashboardMetricsDraggableList from "@rilldata/web-common/components/menu/DashboardMetricsDraggableList.svelte"; export let exploreName: string; @@ -16,24 +16,24 @@ selectors: { measures: { leaderboardMeasureCount, - visibleMeasures, leaderboardMeasureName, getMeasureByName, + visibleMeasures, }, dimensions: { visibleDimensions, allDimensions }, - sorting: { sortByMeasure }, }, actions: { - dimensions: { toggleDimensionVisibility }, contextColumn: { setContextColumn }, - sorting: { toggleSort, setSortDescending }, + dimensions: { setDimensionVisibility, toggleDimensionVisibility }, setLeaderboardMeasureCount, setLeaderboardMeasureName, }, } = StateManagers; - const { leaderboardMeasureCount: leaderboardMeasureCountFeatureFlag } = - featureFlags; + const { + leaderboardMeasureCount: leaderboardMeasureCountFeatureFlag, + reorderMeasuresDimensions, + } = featureFlags; $: measures = getSimpleMeasures($visibleMeasures); @@ -73,19 +73,30 @@ class="flex flex-row items-center ui-copy-muted gap-x-1" style:max-width="768px" > - toggleDimensionVisibility(allDimensionNames, name)} - selectableItems={$allDimensions.map(({ name, displayName }) => ({ - name: name || "", - label: displayName || name || "", - }))} - selectedItems={visibleDimensionsNames} - onToggleSelectAll={() => { - toggleDimensionVisibility(allDimensionNames); - }} - /> + {#if $reorderMeasuresDimensions} + + setDimensionVisibility(items, allDimensionNames)} + allItems={$allDimensions} + selectedItems={visibleDimensionsNames} + /> + {:else} + + toggleDimensionVisibility(allDimensionNames, name)} + selectableItems={$allDimensions.map(({ name, displayName }) => ({ + name: name || "", + label: displayName || name || "", + }))} + selectedItems={visibleDimensionsNames} + onToggleSelectAll={() => { + toggleDimensionVisibility(allDimensionNames); + }} + /> + {/if} {#if $leaderboardMeasureCountFeatureFlag} { setLeaderboardMeasureCount(count); }} - resetSort={() => { - // Fallback to the first visible measure if sort_by measure is not in the context - toggleSort(SortType.VALUE, $visibleMeasures[0].name); - setSortDescending(); - }} - sortByMeasure={$sortByMeasure} /> {:else} { + dashboard.visibleDimensionKeys = new Set(dimensions); + + dashboard.allDimensionsVisible = + dashboard.visibleDimensionKeys.size === allDimensions?.length; +}; + export const dimensionActions = { /** * Sets the primary dimension for the dashboard, which @@ -45,4 +56,5 @@ export const dimensionActions = { */ setPrimaryDimension, toggleDimensionVisibility, + setDimensionVisibility, }; diff --git a/web-common/src/features/dashboards/state-managers/actions/measures.ts b/web-common/src/features/dashboards/state-managers/actions/measures.ts index aad4b79ecbf..e05d5d5d05e 100644 --- a/web-common/src/features/dashboards/state-managers/actions/measures.ts +++ b/web-common/src/features/dashboards/state-managers/actions/measures.ts @@ -30,6 +30,18 @@ export const toggleMeasureVisibility = ( dashboard.visibleMeasureKeys.size === allMeasures.length; }; +export const setMeasureVisibility = ( + { dashboard }: DashboardMutables, + measures?: string[], + allMeasures?: string[], +) => { + dashboard.visibleMeasureKeys = new Set(measures); + + dashboard.allMeasuresVisible = + dashboard.visibleMeasureKeys.size === allMeasures?.length; +}; + export const measureActions = { toggleMeasureVisibility, + setMeasureVisibility, }; diff --git a/web-common/src/features/dashboards/state-managers/selectors/dimensions.ts b/web-common/src/features/dashboards/state-managers/selectors/dimensions.ts index 8abe4391282..f9fab6fa70c 100644 --- a/web-common/src/features/dashboards/state-managers/selectors/dimensions.ts +++ b/web-common/src/features/dashboards/state-managers/selectors/dimensions.ts @@ -29,15 +29,11 @@ export const visibleDimensions = ({ }: DashboardDataSources): MetricsViewSpecDimensionV2[] => { if (!validMetricsView?.dimensions || !validExplore?.dimensions) return []; - return ( - validMetricsView.dimensions - .filter((d) => dashboard.visibleDimensionKeys.has(d.name!)) - // Sort the filtered dimensions based on their order in validExplore.dimensions - .sort( - (a, b) => - validExplore.dimensions!.indexOf(a.name!) - - validExplore.dimensions!.indexOf(b.name!), - ) + return Array.from(dashboard.visibleDimensionKeys).map( + (key) => + validMetricsView.dimensions?.find( + (d) => d.name === key, + ) as MetricsViewSpecDimensionV2, ); }; diff --git a/web-common/src/features/dashboards/state-managers/selectors/measures.ts b/web-common/src/features/dashboards/state-managers/selectors/measures.ts index c8e19ded43f..51b1c7db6b2 100644 --- a/web-common/src/features/dashboards/state-managers/selectors/measures.ts +++ b/web-common/src/features/dashboards/state-managers/selectors/measures.ts @@ -45,15 +45,11 @@ export const visibleMeasures = ({ }: DashboardDataSources): MetricsViewSpecMeasureV2[] => { if (!validMetricsView?.measures || !validExplore?.measures) return []; - return ( - validMetricsView.measures - .filter((m) => dashboard.visibleMeasureKeys.has(m.name!)) - // Sort the filtered measures based on their order in validExplore.measures - .sort( - (a, b) => - validExplore.measures!.indexOf(a.name!) - - validExplore.measures!.indexOf(b.name!), - ) + return Array.from(dashboard.visibleMeasureKeys).map( + (key) => + validMetricsView.measures?.find( + (m) => m.name === key, + ) as MetricsViewSpecMeasureV2, ); }; diff --git a/web-common/src/features/dashboards/time-series/MetricsTimeSeriesCharts.svelte b/web-common/src/features/dashboards/time-series/MetricsTimeSeriesCharts.svelte index 9b60af3d2c4..967805000b9 100644 --- a/web-common/src/features/dashboards/time-series/MetricsTimeSeriesCharts.svelte +++ b/web-common/src/features/dashboards/time-series/MetricsTimeSeriesCharts.svelte @@ -3,7 +3,6 @@ import SimpleDataGraphic from "@rilldata/web-common/components/data-graphic/elements/SimpleDataGraphic.svelte"; import { Axis } from "@rilldata/web-common/components/data-graphic/guides"; import { bisectData } from "@rilldata/web-common/components/data-graphic/utils"; - import DashboardVisibilityDropdown from "@rilldata/web-common/components/menu/DashboardVisibilityDropdown.svelte"; import { LeaderboardContextColumn } from "@rilldata/web-common/features/dashboards/leaderboard-context-column"; import ReplacePivotDialog from "@rilldata/web-common/features/dashboards/pivot/ReplacePivotDialog.svelte"; import { splitPivotChips } from "@rilldata/web-common/features/dashboards/pivot/pivot-utils"; @@ -47,6 +46,9 @@ getOrderedStartEnd, updateChartInteractionStore, } from "./utils"; + import DashboardMetricsDraggableList from "@rilldata/web-common/components/menu/DashboardMetricsDraggableList.svelte"; + import { featureFlags } from "@rilldata/web-common/features/feature-flags"; + import DashboardVisibilityDropdown from "@rilldata/web-common/components/menu/DashboardVisibilityDropdown.svelte"; export let exploreName: string; export let workspaceWidth: number; @@ -64,11 +66,13 @@ dimensionFilters: { includedDimensionValues }, }, actions: { - measures: { toggleMeasureVisibility }, + measures: { setMeasureVisibility, toggleMeasureVisibility }, }, validSpecStore, } = getStateManagers(); + const { reorderMeasuresDimensions } = featureFlags; + const timeControlsStore = useTimeControlStore(getStateManagers()); const timeSeriesDataStore = useTimeSeriesDataStore(getStateManagers()); @@ -106,8 +110,6 @@ $: isAlternateChart = tddChartType !== TDDChart.DEFAULT; $: expandedMeasure = $getMeasureByName(expandedMeasureName); - // List of measures which will be shown on the dashboard - // List of measures which will be shown on the dashboard let renderedMeasures: MetricsViewSpecMeasureV2[]; $: { renderedMeasures = expandedMeasure ? [expandedMeasure] : $visibleMeasures; @@ -303,19 +305,29 @@ chartType={tddChartType} /> {:else} - toggleMeasureVisibility(allMeasureNames, name)} - selectableItems={$allMeasures.map(({ name, displayName }) => ({ - name: name || "", - label: displayName || name || "", - }))} - selectedItems={visibleMeasureNames} - onToggleSelectAll={() => { - toggleMeasureVisibility(allMeasureNames); - }} - /> + {#if $reorderMeasuresDimensions} + + setMeasureVisibility(items, allMeasureNames)} + allItems={$allMeasures} + selectedItems={visibleMeasureNames} + /> + {:else} + toggleMeasureVisibility(allMeasureNames, name)} + selectableItems={$allMeasures.map(({ name, displayName }) => ({ + name: name || "", + label: displayName || name || "", + }))} + selectedItems={visibleMeasureNames} + onToggleSelectAll={() => { + toggleMeasureVisibility(allMeasureNames); + }} + /> + {/if} {#if !hideStartPivotButton}